GuidesLOCAL APIS

Fix the Yelp API CORS error

Yelp's API does allow browser calls: on 28 October 2026 its preflight answered 204 with Access-Control-Allow-Origin: *, and Chromium read Yelp's JSON whenever the key had the right 128-character format. The CORS error appears when Yelp rejects a request before its key check, as with a missing or malformed Authorization header or a limit above 50, because those 400 responses carry no CORS headers. The free cors.dev proxy refuses Authorization; cors.dev Pro, $5 a month with a 7-day trial of 1,000 requests and no card, forwards it and adds CORS headers to every Yelp response, but your key stays visible either way.

What api.yelp.com sends a browser

The Yelp Places API, formerly Yelp Fusion, does send CORS headers, but only on part of its responses. We called https://api.yelp.com/v3/businesses/search on 28 October 2026 from Chromium on a US residential connection, from a cloud machine and through cors.dev. The preflight answered 204 with Access-Control-Allow-Origin: *, allowed the Authorization header and set Access-Control-Max-Age: 1728000.

With a fake key in Yelp's 128-character format, Chromium read Yelp's 401 JSON for /v3/businesses/search, /v3/businesses/{id} and POST /v3/graphql: {"error": {"code": "UNAUTHORIZED_ACCESS_TOKEN", ...}}.

What api.yelp.com returned to a browser origin, checked October 2026
RequestYelp statusCORS headersWhat your code sees
Preflight OPTIONS for Authorization204Allow-Origin *, allows AuthorizationPasses
GET with a fake 128-character key401 UNAUTHORIZED_ACCESS_TOKENYesReadable JSON, 138 bytes
POST /v3/graphql with that key401YesReadable JSON
GET without Authorization400 VALIDATION_ERRORNoneTypeError: Failed to fetch
GET with Bearer undefined or a quoted key400 VALIDATION_ERRORNoneTypeError: Failed to fetch
GET with limit=51 or radius=40001400 VALIDATION_ERRORNoneTypeError: Failed to fetch
Fake key with credentials: include204 preflightWildcard refused by ChromeTypeError: Failed to fetch
Any request from our cloud machine403, empty bodyNoneTypeError: Failed to fetch
Free cors.dev proxy, no key400 VALIDATION_ERRORAdded by cors.devReadable JSON, 140 bytes
Free cors.dev proxy with Authorization400 header_not_allowedAdded by cors.devReadable JSON from cors.dev

Why fetch() to Yelp fails

Yelp checks the request format before it checks the key, and those 400 VALIDATION_ERROR responses carry no CORS headers. The browser hides the body and throws TypeError: Failed to fetch, and Chrome logs No 'Access-Control-Allow-Origin' header is present on the requested resource. The JSON you never see names the exact problem:

  • Missing header: "Authorization is a required parameter."
  • Wrong key format: "'Bearer abc123' does not match", followed by Yelp's pattern: Bearer plus exactly 128 letters, digits, hyphens or underscores. So Bearer undefined from an environment variable that never reached the client bundle, quotes around the key or a missing Bearer prefix all fail here. A lowercase bearer passes.
  • Out-of-range parameters: "51 is greater than the maximum of 50" for limit, and "40001 is greater than the maximum of 40000" for radius.
  • Credentials: credentials: 'include', or withCredentials: true in axios, fails even with a valid header. Chrome says the Allow-Origin value "must not be the wildcard '*' when the request's credentials mode is 'include'". Yelp needs no cookies, so drop the option; the Access-Control-Allow-Credentials guide explains the rule.
  • Edge refusal: a 403 with an empty body comes from Yelp's Varnish edge, not its API. Our cloud machine got it on every request, preflights included, even from Chromium. On the residential connection Chromium got through while curl still got 403, so try another network before you debug the key.

Chrome also logs Failed to load resource: the server responded with a status of 400, which tells you which case you hit.

Call Yelp from the browser

With a well-formed key and a network Yelp's edge accepts, plain fetch() works and you need no proxy. The snippet checks the key format first, so a missing variable fails with a clear message instead of a CORS error, and it reads the body of any error, including an empty 403.

Yelp business search with plain fetch() and a key format check
const yelpKey = 'YOUR_YELP_API_KEY';

// Yelp drops its CORS headers when the key is malformed, so we check the format first
if (!/^[\w-]{128}$/.test(yelpKey)) {
  throw new Error(`A Yelp key is 128 letters, digits, - or _, not ${yelpKey.length} characters`);
}

const params = new URLSearchParams({ location: 'Brooklyn, NY', term: 'pizza', limit: '5' });
const response = await fetch(`https://api.yelp.com/v3/businesses/search?${params}`, {
  headers: { Authorization: `Bearer ${yelpKey}` },
});

if (!response.ok) {
  const body = await response.text();
  throw new Error(`Yelp answered ${response.status}: ${body.slice(0, 200) || 'empty body'}`);
}

const { businesses, total } = await response.json();
console.log(`${total} matches`);
for (const business of businesses) {
  console.log(`${business.name}: ${business.rating} stars, ${business.review_count} reviews, ${business.url}`);
}

Two gaps remain. Yelp's CORS headers include no Access-Control-Expose-Headers, so this code cannot read the RateLimit-Remaining header Yelp documents on each response. And the key sits in your page for anyone to copy.

Your Yelp key is a secret

Yelp's authentication docs say: "Please keep the API Key to yourself since it is the credential for your call to Yelp's API." A key in browser code is visible in DevTools to every visitor, who can then spend your calls. If one leaks, refresh it on Yelp's Manage App page.

Those calls cost money. Yelp ended free access in July 2024, when it moved existing free users to paid licences, and its pricing page, updated 12 October 2026, lists three plans:

  • Base: $229 a month plus $5.91 per extra 1,000 calls, with no photos or review excerpts.
  • Enhanced: $299 a month plus $6.57 per extra 1,000 calls, with up to 3 photos and 3 review excerpts.
  • Premium: $643 a month plus $14.13 per extra 1,000 calls, with up to 12 photos, 7 review excerpts and Review Highlights.

Each plan starts with a 30-day trial of 5,000 calls that needs a business email and is "strictly for evaluation and not for commercial deployment". Yelp's FAQ says paid plans include 30,000 calls a month with up to 5,000 a day, and above 150,000 a month you talk to sales. For a public app, keep the key on your own backend; the proxy versus backend comparison covers the options.

Where cors.dev helps

The free proxy, https://proxy.cors.dev/ in front of the URL, cannot carry the key: it answers an Authorization header with 400 header_not_allowed and "Authorization is only accepted with a Pro key". Called without a key, it still returns Yelp's 400 body with CORS headers, so you can read the validation message the browser hides.

cors.dev Pro forwards Authorization from origins you register. Every Yelp response comes back with CORS headers, the 400s included, and cors.dev lists each upstream header in Access-Control-Expose-Headers, so RateLimit-Remaining becomes readable. From the cloud machine where Yelp's edge answered 403, the same request through cors.dev reached Yelp and returned its JSON error.

  1. Start the trial

    Sign in with Google at cors.dev/signup for 7 days and 1,000 requests, no card.

  2. Register your origin

    Add http://localhost:5173 or your app's origin and copy the Connection key.

  3. Send both headers

    Put the Connection key in X-Cors-Key and your Yelp key in Authorization: Bearer, and call https://proxy.cors.dev/https://api.yelp.com/v3/....

Yelp search through cors.dev Pro with readable errors and RateLimit-Remaining
const PROXY = 'https://proxy.cors.dev/';
const yelpKey = 'YOUR_YELP_API_KEY';

const params = new URLSearchParams({ location: 'Austin, TX', term: 'tacos', limit: '5' });
const response = await fetch(`${PROXY}https://api.yelp.com/v3/businesses/search?${params}`, {
  headers: { 'X-Cors-Key': 'YOUR_CONNECTION_KEY', Authorization: `Bearer ${yelpKey}` },
});

// cors.dev exposes Yelp's headers and error bodies, so both are readable here
const text = await response.text();
if (!response.ok) {
  throw new Error(`${response.headers.get('X-Cors-Source')} answered ${response.status}: ${text.slice(0, 200)}`);
}

console.log('Calls left today:', response.headers.get('RateLimit-Remaining'));
for (const business of JSON.parse(text).businesses) {
  console.log(`${business.name}, ${business.rating} stars, ${business.location.display_address.join(', ')}`);
}

The Connection key is publishable and only works from your registered origins, but cors.dev does not hide the Yelp key: it still travels from the browser. Use cors.dev Pro for internal tools and prototypes, and a backend for production.

Limits to plan for

  • Yelp quotas: The daily limit answers 429 ACCESS_LIMIT_REACHED and resets at midnight UTC; calling too fast answers 429 TOO_MANY_REQUESTS_PER_SECOND.
  • Search size: Up to 50 businesses per request and 240 per query with offset. Businesses without reviews never appear.
  • Caching and display: Cache Yelp content for at most 24 hours; business IDs may be stored indefinitely. Show the Yelp logo linked to Yelp, use Yelp-branded stars next to the review count, link ratings and excerpts back to Yelp and never blend Yelp ratings with other sources.
  • cors.dev: The free tier allows GET and HEAD at about 600 requests a minute per IP. cors.dev Pro allows 500,000 requests a month at 3,000 requests a minute per account and 100 in flight. Both cap responses at 6 MiB with a 30-second upstream deadline.

Good questions.

Does the Yelp API support CORS?

Yes, with gaps. api.yelp.com answers preflights with Access-Control-Allow-Origin: *, allows Authorization, and sends the same headers on its 401 errors. Its 400 VALIDATION_ERROR responses carry none, so a missing or malformed key or a bad parameter shows up as a CORS error.

Why does api.yelp.com return 403 with an empty body?

That 403 comes from Yelp's Varnish edge, which refused your network or client before the request reached the API. Our cloud machine got it on every request, even from Chromium, while Chromium on a residential connection did not. Try another network before you change the key.

Is the Yelp API free?

No. Yelp ended free access in July 2024, and plans now run from $229 a month for Base to $643 for Premium. A 30-day trial gives 5,000 calls for evaluation only and needs a business email.

Can I keep my Yelp key in frontend code?

Only in internal tools or prototypes where you trust everyone who opens the page. Anyone with DevTools can copy a key used in browser JavaScript, with or without cors.dev, and spend your paid calls. Production apps should call Yelp from their own backend.

Does the free cors.dev proxy work with the Yelp API?

Not for real searches: it refuses the Authorization header with 400 header_not_allowed. Without a key it still shows Yelp's hidden validation errors. To forward the key, use cors.dev Pro with an X-Cors-Key header.