Data & analysis

Scavio Yelp

Try it

Search Yelp businesses in a metro, pull one business in full with hours, amenities and health inspections, and page through review bodies with owner responses. 3 endpoints, 2 credits each, structured JSON.

What it does

Search Yelp businesses in a metro, pull one business in full with hours, amenities and health inspections, and page through review bodies with owner responses. 3 endpoints, 2 credits each, structured JSON.

The skill document

Yelp via Scavio

Search Yelp businesses in a metro, read one business in full, and page through its reviews. All endpoints return structured JSON.

When to trigger

Use this skill when the user asks to:

  • Find local businesses by term and location, in Yelp's ranked order
  • Filter by price band, open-now, or Yelp's own attribute aliases
  • Pull one business in full - rating histogram, price band, categories, address and coordinates, phone, website and menu links, hours and holidays, amenities, photos, popular items, health inspections, Q&A, licences, claim status
  • Read review bodies with author expertise, attached photos, reaction counts and the owner's response
  • Build local-lead lists, reputation monitors or competitor comp sets

Setup

Get a free API key at scavio.dev (50 free credits to get started, no card required):

export SCAVIO_API_KEY=sk_live_your_key

Every request is a POST with a JSON body and:

Authorization: Bearer $SCAVIO_API_KEY

Endpoints

Base URL: https://api.scavio.dev. Every Yelp endpoint costs 2 credits.

EndpointCreditsWhat it returns
POST /api/v1/yelp/search2Businesses in Yelp's ranked order: rating, review count, price band, categories, address, contact rails, hours, photos, review snippet; each row carries business_id and alias
POST /api/v1/yelp/business2One business in full - plus the first page of reviews at no extra cost
POST /api/v1/yelp/reviews2A page of reviews: rating, full text, language, author profile and expertise counts, attached photos, reaction counts, owner response

Workflow

  1. Find businesses: call /yelp/search with term and location (or a full yelp.com/search URL as url). Read businesses[].business_id or alias.
  2. One business: call /yelp/business with that business_id - an alias like desnudo-coffee-austin-2, the opaque encid, or a yelp.com/biz URL all work. This already includes page 1 of the reviews.
  3. More reviews: call /yelp/reviews starting at page: 2.

The two ways to waste 2 credits

  • /yelp/reviews with page: 1 re-fetches the document /business already returned. It is the same content for another 2 credits. Always start review paging at page 2.
  • A search without location is answered off the proxy exit, so the same request reports on a different metro from one run to the next. location is effectively required even though the schema allows a url instead.

Pagination

Yelp fixes the page size at 10 for both search and reviews.

  • /search takes a 1-based page. count is the 10-row page, total_results is Yelp's headline count, results_per_page and start are echoed back.
  • /reviews takes a 1-based page and carries has_next_page. A page past the last review is a 404, not an empty result - stop on has_next_page: false.
  • /business takes no paging parameter.

Parameters

ParameterTypeDefaultDescription
termstringwith locationWhat to look for (1-200 chars)
locationstringwith termMetro, neighbourhood or address (1-200 chars)
urlstringalternativeFull yelp.com/search URL instead of term + location (1-1000 chars)
pageinteger--1-based, page size fixed at 10
sortstringrecommendedrecommended, rating, review_count
priceinteger[]--Any of 1, 2, 3, 4 (1-4 items)
open_nowboolean--Open at request time
attributesstring[]--Raw Yelp filter aliases, max 20 (RestaurantsDelivery, GoodForKids, WheelchairAccessible)

(term AND location) or url is required.

sort is closed - Yelp ignores an unrecognised sort and serves default ranking under a 200, so an invented value buys a premium scrape of a sort that never ran. attributes is the opposite: a deliberate passthrough, because Yelp's vocabulary runs to roughly 117 aliases per vertical. An alias Yelp does not know is ignored upstream and the results come back unfiltered - so check that the filter you asked for is reflected in the results before reporting it as applied.

Business (/business)

ParameterTypeDefaultDescription
business_idstringone ofAlias (desnudo-coffee-austin-2), opaque encid, or a yelp.com/biz URL
urlstringone ofFull yelp.com/biz URL

Reviews (/reviews)

ParameterTypeDefaultDescription
business_idstringone ofAlias, encid, or biz URL
urlstringone ofFull yelp.com/biz URL
pageinteger--Start at 2 - page 1 is what /business already returned
sortstringrelevancerelevance, newest, oldest, rating_high, rating_low, elites
ratinginteger--1-5. Changes filtered_review_count, not review_count

Examples

import requests

BASE = "https://api.scavio.dev"
# Your key from https://scavio.dev. Load it from your environment or secret
# store in real code - keep it out of source control.
API_KEY = "sk_your_key_here"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1. Always send location - a location-less search follows the proxy exit
found = requests.post(f"{BASE}/api/v1/yelp/search", headers=HEADERS,
    json={"term": "coffee", "location": "Austin, TX",
          "sort": "review_count", "price": [1, 2], "open_now": True}).json()

row = found["data"]["businesses"][0]

# 2. Full business record - page 1 of the reviews rides along free
biz = requests.post(f"{BASE}/api/v1/yelp/business", headers=HEADERS,
    json={"business_id": row["alias"]}).json()["data"]

print(biz["rating"], biz["review_count"], biz["not_recommended_review_count"])
first_page_reviews = biz["reviews"]

# 3. Page PAST it. Never ask for page 1 here - it is the same 2 credits twice.
more = requests.post(f"{BASE}/api/v1/yelp/reviews", headers=HEADERS,
    json={"business_id": row["alias"], "page": 2, "sort": "newest"}).json()["data"]

# 4. One-star reviews only: filtered_review_count is the count that matches
one_star = requests.post(f"{BASE}/api/v1/yelp/reviews", headers=HEADERS,
    json={"business_id": row["alias"], "rating": 1, "page": 2}).json()["data"]
print(one_star["filtered_review_count"], "of", one_star["review_count"])

Paging reviews, stopping on the signal Yelp actually gives:

def review_pages(business_id, start=2, max_pages=4, **body):
    """10 per page, 2 credits per page. Page 1 came free with /business."""
    out = []
    for page in range(start, start + max_pages):
        data = requests.post(f"{BASE}/api/v1/yelp/reviews", headers=HEADERS,
                             json={"business_id": business_id, "page": page, **body}).json()["data"]
        out += data["reviews"]
        if not data["has_next_page"]:      # past the last page is a billed 404
            break
    return out

Response shapes

Every response uses the envelope { data, response_time, credits_used, credits_remaining }. Key data fields:

  • searchterm, location, url, location_display, city, state, country, latitude, longitude, bounds, categories, vertical, sort, attributes, open_now, breadcrumbs, total_results, results_per_page, page, start, related_searches, count, businesses[] (pos, business_id, alias, name, url, rank, is_ad, rating, review_count, price_range, categories, address, street_address, city, state, postal_code, country, neighborhoods, timezone, hours, image, images, photos, snippet, tags, actions, highlights, is_yelp_guaranteed, has_verified_license, is_service_area_business).
  • businessbusiness_id, alias, name, url, summary, specialties, history, year_established, rating, review_count, rating_distribution, not_recommended_review_count, review_counts_by_language, price_range, categories, phone, website, menu_url, address, city, state, postal_code, country, neighborhoods, timezone, latitude, longitude, service_areas, hours[], hours_today, is_open_now, is_closed, holidays, attributes, amenities, popular_items[], popular_items_omitted, review_highlights, health_inspections, question_count, questions, is_claimed, is_advertiser, verified_licenses, messaging_response_time, filtered_review_count, reviews[] (page 1).
  • reviewsbusiness_id, alias, name, url, rating, review_count, rating_distribution, filtered_review_count, sort, rating_filter, start, page, has_next_page, count, reviews[] (pos, review_id, url, rating, text, language, published_date, experience_date, author, author_url, author_photo, author_expertise, author_expertise_count, photos, videos, reactions, is_first_review, previous_review_count, owner_response, owner_response_date, owner_response_from, appreciated_by_owner).
{
  "data": {
    "term": "coffee",
    "location": "Austin, TX",
    "page": 1,
    "start": 0,
    "results_per_page": 10,
    "total_results": 240,
    "count": 10,
    "businesses": [
      {
        "pos": 1,
        "business_id": "kQ7pCn2m0Vx9",
        "alias": "desnudo-coffee-austin-2",
        "name": "Desnudo Coffee",
        "rating": 4.5,
        "review_count": 2153,
        "price_range": "$",
        "categories": ["Coffee & Tea"],
        "city": "Austin",
        "state": "TX",
        "url": "https://www.yelp.com/biz/desnudo-coffee-austin-2"
      }
    ]
  },
  "credits_used": 2,
  "credits_remaining": 998
}

Guardrails

  • Every call is 2 credits, including one that comes back empty. Yelp is on the premium proxy table - budget before paging.
  • Never call /reviews with page: 1. /business already returned that page; the repeat is 2 credits for a duplicate.
  • Always send location. Without it the answer is about wherever the request exited, and it can differ between two identical runs.
  • Never invent a sort value. An unrecognised sort is ignored and you pay for default ranking.
  • attributes is a passthrough, so an alias Yelp does not recognise silently disappears. Verify the filter took effect before telling the user it was applied.
  • rating on /reviews changes filtered_review_count, not review_count. Quote the right one.
  • Yelp's recommendation software hides some reviews entirely. Those are never returned and are counted in not_recommended_review_count - so the reviews you can read are not the whole picture, and an average you compute yourself will not match Yelp's.
  • popular_items can arrive as stub shells; those rows are dropped and popular_items_omitted flags it. If that flag is true, do not claim the item list is complete.
  • Never fabricate business names, ratings, addresses, hours or review text. Only return what the API returned.
  • Always include the business url so the user can verify.

Failure handling

  • 400 means an invalid or missing parameter - term without location and no url, a sort outside its enum, or more than 20 attributes. Fix and retry.
  • 401 means the API key is invalid or missing. Check SCAVIO_API_KEY.
  • 404 means the business does not exist, or the review page is past the last one. Stop on has_next_page: false rather than discovering the end with a billed 404.
  • 429 means rate or usage limit exceeded. Wait before retrying. See https://scavio.dev/docs/rate-limits.
  • 502 / 503 mean upstream is temporarily unavailable - wait a few seconds and retry, up to a few times.
  • An empty search is usually the filters: drop open_now, widen price, or use a broader term.
  • If SCAVIO_API_KEY is not set, prompt the user to export it before continuing.

SDKs

pip install scavio==0.15.0
from scavio import ScavioClient

client = ScavioClient()  # reads SCAVIO_API_KEY

found = client.yelp.search(term="coffee", location="Austin, TX", sort="review_count")
alias = found["data"]["businesses"][0]["alias"]

biz = client.yelp.business(business_id=alias)          # includes page 1 of reviews
more = client.yelp.reviews(business_id=alias, page=2)  # start at 2, never 1
npm install scavio@0.15.0
import { Scavio } from "scavio";

const client = new Scavio(); // reads SCAVIO_API_KEY
const found = await client.yelp.search({ term: "coffee", location: "Austin, TX" });
const more = await client.yelp.reviews({ business_id: found.data.businesses[0].alias, page: 2 });

Related skills

A Yelp API alternative on fetcher.sh — pay-per-call in USDC via x402, or prepaid credits with a Bearer key, no Yelp Fusion API app approval. Use when the user wants to search local businesses by query and location sorted by rating or review count, fetch a business's full details by ID or by its Yelp URL handle/slug, or fetch a business's reviews. Also covers local business discovery, restaurant/service research, review sentiment input, and competitor monitoring for local businesses without Yelp Fusion API's app-approval process and daily call caps.

1 installs

Researches restaurants and grocery/food delivery via the Crawlora API — Yelp reviews, OpenTable reservations, DoorDash and Uber Eats restaurant search/menus, and Instacart grocery search — returning clean JSON. Use when the user wants restaurant reviews or menus, delivery options near a location, or grocery product/store search.

1 installs

Local SEO analysis covering Google Business Profile optimization, NAP consistency, citation health, review signals, local schema markup, location page quality, multi-location SEO, and industry-specific recommendations. Detects business type (brick-and-mortar, SAB, hybrid) and industry vertical. Use when user says "local SEO", "Google Business Profile", "GBP", "map pack", "local pack", "citations", "NAP consistency", "service area", or "multi-location".

Discovers restaurant brands, food businesses, and hospitality accounts on Instagram using apidojo's Instagram Scraper on Apify. Triggers when the user asks to: find restaurant brands on Instagram, discover food businesses for outreach on Instagram, build a list of restaurant Instagram accounts, find cafes or food chains active on Instagram, identify local restaurant brands by hashtag or location on Instagram, or prospect food and beverage businesses via their Instagram presence. Returns account handle, follower count, bio, post count, and engagement data. Ideal for food tech SaaS vendors, beverage distributors, and B2B service providers targeting restaurants.

1 installs

Resolve any place or business name to Tripadvisor ids, then pull ranked restaurants, hotels and attractions in a geo, one location in full, and paged review bodies. 4 endpoints, 2 credits each, structured JSON.