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:

  1. Any origin can read the response.
  2. 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, or POST
  • 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-Origin
  • Access-Control-Allow-Methods
  • Access-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.