If you call the Wix API from browser JavaScript, CORS is the gatekeeper. When it’s configured the way your frontend needs, everything feels normal. When it isn’t, you get the classic useless browser error: blocked by CORS policy.
This guide is the version I wish I had the first time I tried wiring a frontend directly to a third-party API.
What CORS means for Wix API
CORS stands for Cross-Origin Resource Sharing. Browsers enforce it when your page on one origin tries to call an API on another origin.
Example:
- Your frontend runs on
https://my-site.com - You call
https://www.wixapis.com/...
That’s cross-origin, so the browser checks the response headers from Wix before it lets your JavaScript read the response.
The header that matters most is:
Access-Control-Allow-Origin: https://my-site.com
Or sometimes:
Access-Control-Allow-Origin: *
That wildcard means “any origin can read this response” — with limitations I’ll get into in a minute.
For comparison, here’s a real-world public API CORS example from api.github.com:
access-control-allow-origin: *
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
That tells the browser two things:
- Any origin can read the response.
- JavaScript is allowed to read those listed response headers too.
That second part matters more than people expect.
The two Wix API situations
With Wix API, you’ll usually end up in one of these buckets:
1. Public-safe browser call
You’re calling an endpoint that is intentionally usable from the browser, and the CORS headers allow it.
Typical frontend code:
const res = await fetch("https://www.wixapis.com/some/public/endpoint", {
method: "GET",
headers: {
"Accept": "application/json"
}
});
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const data = await res.json();
console.log(data);
If the response includes a matching Access-Control-Allow-Origin, your app can read it.
2. Authenticated or sensitive call
You need an API key, OAuth token, app secret, or some privileged server-side credential.
Do not put that secret in browser JavaScript and hope CORS will save you. It won’t.
CORS is a browser read restriction, not a secret-protection mechanism. If a token is in frontend code, it’s already exposed.
For sensitive Wix API calls, use your own backend as a proxy:
// frontend
const res = await fetch("/api/wix/orders");
const data = await res.json();
console.log(data);
// backend example (Node/Express)
app.get("/api/wix/orders", async (req, res) => {
const wixRes = await fetch("https://www.wixapis.com/stores/v1/orders", {
headers: {
"Authorization": `Bearer ${process.env.WIX_ACCESS_TOKEN}`,
"Content-Type": "application/json"
}
});
const text = await wixRes.text();
res.status(wixRes.status).type("application/json").send(text);
});
That’s the safe pattern almost every time credentials are involved.
The browser flow you need to recognize
CORS failures are easier to debug when you know whether the browser sent:
- a simple request
- or a preflighted request
Simple request
A request may go straight through if it uses:
GET,HEAD, orPOST- only safelisted headers
- a safelisted content type
Example:
fetch("https://www.wixapis.com/some/endpoint");
If Wix returns the right Access-Control-Allow-Origin, the browser allows your script to read the response.
Preflight request
If you send custom headers like Authorization, or use methods like PUT, PATCH, or DELETE, the browser usually sends an OPTIONS request first.
Example that likely triggers preflight:
await fetch("https://www.wixapis.com/some/endpoint", {
method: "POST",
headers: {
"Authorization": "Bearer token",
"Content-Type": "application/json"
},
body: JSON.stringify({ hello: "world" })
});
The browser first asks Wix something like:
OPTIONS /some/endpoint HTTP/1.1
Origin: https://my-site.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type
Wix must answer with headers like:
Access-Control-Allow-Origin: https://my-site.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: authorization, content-type
If that preflight response doesn’t line up, your actual request never gets sent.
Copy-paste frontend examples
Basic GET to Wix API
async function getWixData() {
const res = await fetch("https://www.wixapis.com/example/v1/resources", {
method: "GET",
headers: {
"Accept": "application/json"
}
});
if (!res.ok) {
const errorText = await res.text();
throw new Error(`Wix API error ${res.status}: ${errorText}`);
}
return res.json();
}
getWixData()
.then(console.log)
.catch(console.error);
Request with bearer token from backend only
Do this on your server, not in the browser:
async function getWixMembers() {
const res = await fetch("https://www.wixapis.com/members/v1/members", {
headers: {
"Authorization": `Bearer ${process.env.WIX_ACCESS_TOKEN}`,
"Accept": "application/json"
}
});
if (!res.ok) {
throw new Error(`Wix API error: ${res.status}`);
}
return res.json();
}
Frontend calling your backend proxy
async function loadMembers() {
const res = await fetch("/api/members", {
credentials: "include"
});
if (!res.ok) {
throw new Error(`Backend error: ${res.status}`);
}
return res.json();
}
Express proxy with explicit CORS
If your frontend and backend are on different origins, your backend also needs CORS headers:
import express from "express";
const app = express();
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://my-site.com");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (req.method === "OPTIONS") {
return res.sendStatus(204);
}
next();
});
app.get("/api/members", async (req, res) => {
const wixRes = await fetch("https://www.wixapis.com/members/v1/members", {
headers: {
"Authorization": `Bearer ${process.env.WIX_ACCESS_TOKEN}`,
"Accept": "application/json"
}
});
const body = await wixRes.text();
res.status(wixRes.status).type("application/json").send(body);
});
app.listen(3000);
Reading response headers from Wix
Even when the network request succeeds, the browser only lets JavaScript read a small set of response headers unless the API exposes more with Access-Control-Expose-Headers.
This works:
const res = await fetch("https://api.github.com");
console.log(res.headers.get("content-type"));
console.log(res.headers.get("etag")); // works because GitHub exposes it
console.log(res.headers.get("x-ratelimit-remaining")); // also exposed
GitHub explicitly exposes headers like:
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
If Wix doesn’t expose a header, your browser code can’t read it even if DevTools shows it in the response.
That mismatch confuses a lot of people.
The wildcard trap
This is the rule people run into constantly:
Access-Control-Allow-Origin: *
works for public cross-origin reads, but not when credentials are involved.
If your fetch uses cookies or HTTP auth:
fetch("https://api.example.com/data", {
credentials: "include"
});
then the server cannot reply with *. It has to send the exact origin:
Access-Control-Allow-Origin: https://my-site.com
Access-Control-Allow-Credentials: true
So if your Wix integration relies on browser cookies or credentialed requests, wildcard CORS won’t cut it.
Common Wix CORS failure patterns
“Works in Postman, fails in browser”
Classic. Postman doesn’t enforce browser CORS rules.
If a Wix API call works in Postman but fails in fetch, that usually means:
- the browser sent a preflight that failed
- the response lacked
Access-Control-Allow-Origin - the API is not meant for direct browser use
“Network tab shows 200 but my code can’t read it”
Usually one of these:
- CORS blocked access to the response
- the response is opaque because of request mode or policy
- the header you want isn’t in
Access-Control-Expose-Headers
“Authorization header causes failure”
That almost always means preflight. The browser asks whether authorization is allowed. If the response doesn’t include:
Access-Control-Allow-Headers: authorization
the browser blocks the actual request.
“I added mode: 'no-cors' and the error disappeared”
That is not a fix.
Example:
fetch("https://www.wixapis.com/some/endpoint", {
mode: "no-cors"
});
You’ll get an opaque response that your code can’t meaningfully read. No JSON, no useful status handling, no real integration. I only use no-cors for very niche cases like fire-and-forget requests, and even then I’m suspicious of it.
How I debug Wix CORS issues
I keep it boring and mechanical.
1. Check the request origin
Make sure the browser page is actually running on the origin you think it is:
- protocol
- host
- port
https://app.example.com and https://example.com are different origins.
2. Inspect the preflight
In DevTools, look for an OPTIONS request before the real call.
Check whether the response includes:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers
3. Compare requested vs allowed headers
If the browser asks for:
Access-Control-Request-Headers: authorization, content-type
the server needs to allow those exact headers, case-insensitively.
4. Check credential mode
If you use:
credentials: "include"
then you need:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://exact-origin.example
Not *.
5. Decide whether this request belongs on the server
This is the one that saves the most time. If the Wix API call needs secrets, move it server-side immediately instead of trying to negotiate your way around CORS.
Secure default for Wix API integrations
If you’re unsure whether a Wix API request should happen in the browser, my default answer is: probably not.
Use this split:
- Browser: public data, no secrets, no privileged tokens
- Backend: OAuth tokens, API keys, admin operations, anything sensitive
That keeps CORS simpler and your security model less fragile.
If you’re also hardening headers on your own backend, CORS is just one piece of the puzzle. CSP, HSTS, and related headers matter too. For that broader side of header security, see the official docs you use internally or, if you need a practical reference, https://csp-guide.com.
Official docs
For Wix-specific endpoint behavior, auth requirements, and whether a given API is designed for frontend or backend use, check the official Wix documentation:
That’s the first place I look before blaming CORS. Half the time the real issue is simpler: the endpoint was never intended to be called directly from browser code.