Back to Documentation
Guides

How to Set Up Your Own API Proxy in 15 Minutes

Build custom Squarespace integrations by setting up a free Cloudflare Workers proxy to connect any API to your site.

This is the unlock for building real, custom integrations in Squarespace. Want to pull in live inventory from a POS system? Display real-time booking availability? Show product reviews from a third-party platform? If a service has an API and auth credentials, this guide will teach you how to connect it to your Squarespace frontend.

The problem is, if you try to set this up in the Code Injection area, you'll hit CORS errors, or worse — your API keys get exposed and open for exploitation. That's a big no-no.

Integrations like this assume you have "server-side" access to your website, but Squarespace doesn't allow for that.

The fix: we simulate "server-side" access through what's called a "proxy server". A proxy server sits between your Squarespace site and the API. It keeps your credentials secret, handles all the CORS nonsense, and opens the door to custom components that go way beyond what native Squarespace blocks can do. This is how you build the kind of integrations that let you charge more and leave your clients genuinely impressed.

No doubt, this is an advanced integration and will require you to get out of your comfort zone and learn more, but career growth ain't easy.

We're using Cloudflare Workers for this. No server to maintain, no monthly bill, and it takes about 15 minutes.


What You'll Need Before We Start

Don't skip this part — get these set up first so you can follow along without hitting a wall.

1. A Cloudflare Account (Free)

Head to cloudflare.com and sign up. You don't need a paid plan. The free tier gives you 100,000 requests per day, which is way more than enough.

2. Node.js Installed

You need Node.js on your computer to use Cloudflare's command line tool. If you're not sure whether you have it:

  • Open your Terminal (Mac) or Command Prompt (Windows)
  • Type node -v and press Enter
  • If you see a version number (like v20.11.0), you're good
  • If you get an error, download it from nodejs.org — grab the LTS version

3. A Code Editor

VS Code is the standard. If you don't have it: code.visualstudio.com. Any editor works though.

4. An API You Want to Call

Have the API URL, any API keys, and the documentation handy. We'll use a generic example, but this works with basically any REST API.


Step 1: Create Your Project

Open your terminal and run:

npm create cloudflare@latest my-api-proxy

It'll ask you a few questions:

  • What type of application? → Select "Hello World" Worker
  • TypeScript? → Select No (we're keeping it simple)
  • Deploy now? → Select No (we'll deploy after we write our code)

Now move into your project folder:

cd my-api-proxy

Step 2: Write the Proxy

Open the project in your code editor. Find the file at src/index.js (or worker.js depending on your version). Delete everything in there and replace it with this:

export default { async fetch(request, env) { // -------------------- // CONFIGURATION // -------------------- // The base URL of the API you want to call const API_BASE_URL = "https://api.example.com"; // Any headers/keys the API needs (these stay secret on the server) const API_HEADERS = { "Authorization": "Bearer YOUR_API_KEY_HERE", "Content-Type": "application/json", }; // Which domains are allowed to use this proxy const ALLOWED_ORIGINS = [ "https://yourwebsite.com", "https://www.yourwebsite.com", "http://localhost:3000", // for local development ]; // -------------------- // DON'T EDIT BELOW THIS LINE (unless you want to) // -------------------- const origin = request.headers.get("Origin") || ""; // Check if the request is coming from an allowed domain if (!ALLOWED_ORIGINS.includes(origin)) { return new Response("Not allowed", { status: 403 }); } // CORS headers we'll attach to every response const corsHeaders = { "Access-Control-Allow-Origin": origin, "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }; // Handle preflight requests (the browser's "can I do this?" check) if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } // Build the URL to forward to // If your proxy is at: https://my-proxy.workers.dev/v1/users // This forwards to: https://api.example.com/v1/users const url = new URL(request.url); const targetUrl = API_BASE_URL + url.pathname + url.search; try { // Forward the request to the real API const apiResponse = await fetch(targetUrl, { method: request.method, headers: { ...API_HEADERS, // Pass along the content-type from the original request "Content-Type": request.headers.get("Content-Type") || "application/json", }, // Forward the body for POST/PUT requests body: ["GET", "HEAD"].includes(request.method) ? null : request.body, }); // Send the API's response back with CORS headers attached const responseBody = await apiResponse.text(); return new Response(responseBody, { status: apiResponse.status, headers: { ...corsHeaders, "Content-Type": apiResponse.headers.get("Content-Type") || "application/json", }, }); } catch (error) { return new Response(JSON.stringify({ error: "Proxy request failed" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } }, };

Step 3: Configure It For Your API

The only part you need to change is the CONFIGURATION section at the top:

  1. API_BASE_URL — Change this to the base URL of whatever API you're calling. Just the base, no trailing slash.

  2. API_HEADERS — Add whatever authentication the API needs. This is the magic part: these keys live on Cloudflare's servers, not in your frontend code. Your website visitors will never see them.

  3. ALLOWED_ORIGINS — List every domain that should be allowed to use this proxy. Keep http://localhost:3000 in there while you're developing. Remove it before going live if you want to lock it down.


Step 4: Test It Locally

Run this in your terminal:

npx wrangler dev

This starts a local version of your proxy (usually at http://localhost:8787). You can test it by opening a new terminal and running:

curl http://localhost:8787/any/api/endpoint

Or from your browser's console on one of your allowed domains:

const response = await fetch("http://localhost:8787/v1/whatever-endpoint"); const data = await response.json(); console.log(data);

Step 5: Deploy It (For Real)

When it's working locally, deploy to Cloudflare:

npx wrangler deploy

The first time, it'll ask you to log in to your Cloudflare account in the browser. After that, it'll give you a URL like:

https://my-api-proxy.YOUR-SUBDOMAIN.workers.dev

That's your proxy URL. Done. It's live.


Step 6: Use It In Your Code

Wherever you were calling the API directly, replace the base URL with your proxy URL:

// Before (this gets blocked by CORS) const response = await fetch("https://api.example.com/v1/data"); // After (goes through your proxy, CORS handled) const response = await fetch("https://my-api-proxy.YOUR-SUBDOMAIN.workers.dev/v1/data");

That's literally it. Your proxy forwards the request, injects your API key, and sends back the response with the right CORS headers.


What Did We Actually Build?

Here's what's happening in plain English:

  1. Your Squarespace site sends a request to your proxy URL
  2. The proxy checks: "Is this request coming from an allowed domain?" If not, it blocks it.
  3. If allowed, the proxy forwards the request to the real API and attaches your secret API key
  4. The API responds to the proxy
  5. The proxy sends the response back to your site with CORS headers so the browser doesn't freak out

Your API key never touches the browser. Your allowed domains are enforced. The browser is happy. You're happy.

This is the foundation for any custom Squarespace integration — booking widgets, live inventory displays, CRM lookups, review feeds, payment flows, you name it. Anything with an API is now fair game.


Quick Troubleshooting

"I'm getting a 403 error" Your domain isn't in the ALLOWED_ORIGINS list. Make sure it matches exactly — https:// vs http://, www vs no www, and trailing slashes matter.

"I'm getting CORS errors still" Make sure ALLOWED_ORIGINS includes the exact origin your browser is sending. Check the browser console → Network tab → look at the Origin header on the failed request.

"It works locally but not deployed" Did you update ALLOWED_ORIGINS to include your production domain? localhost won't work in production.

"The API returns HTML instead of JSON" Double-check your API_BASE_URL. You might have a typo or the API path structure might be different than you expect.


Free Tier Limits (Plenty for Most People)

  • 100,000 requests per day — that's roughly 1 request per second, all day
  • 10ms CPU time per request — proxy requests are fast, you won't hit this
  • KV Storage (if you want to get fancy later) — 100,000 reads/day, 1,000 writes/day

If you outgrow the free tier, the paid plan is $5/month for 10 million requests.


Next Steps (If You Want to Get Fancy)

  • Use Environment Variables for your API key instead of hardcoding it: npx wrangler secret put API_KEY then access it as env.API_KEY in your code
  • Add Rate Limiting to prevent abuse
  • Add Caching for GET requests that don't change often (saves API calls)
  • Add Logging to track usage

But honestly? The basic version above handles 90% of use cases. Ship it, move on, build the thing you actually care about.