mosaddik.dev

How to call an external API from Wix Velo without exposing your API key

Mosaddik Billah

Most Wix integrations come down to one job: send a request to another service and use what comes back. The mistake I see most often is calling that service straight from page code, with the API key pasted in. Page code runs in the visitor’s browser, so anyone can open the developer tools and copy the key.

The fix is to keep the key and the request on the backend. Here’s the whole pattern.

1. Store the key in the Secrets Manager

In your site’s dashboard, open Developer Tools, then Secrets Manager, and add a secret. In this example it’s called WEATHER_API_KEY. The key now lives in Wix’s backend and never appears in your code.

2. Call the API from a backend web module

Create a file in the backend folder that ends in .web.js, for example backend/weather.web.js. Functions exported with webMethod run on the server, and page code can call them.

import { Permissions, webMethod } from "wix-web-module";
import { getSecret } from "wix-secrets-backend";
import { fetch } from "wix-fetch";

export const getForecast = webMethod(Permissions.Anyone, async (city) => {
  const apiKey = await getSecret("WEATHER_API_KEY");
  const url = `https://api.example.com/forecast?city=${encodeURIComponent(city)}`;

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (!response.ok) {
    throw new Error(`Forecast request failed: ${response.status}`);
  }

  const data = await response.json();
  // Return only what the page needs, not the whole response.
  return { summary: data.summary, temperature: data.temperature };
});

Two details matter here:

  • Permissions. Permissions.Anyone lets any visitor trigger the call. If only logged-in members should, use Permissions.SiteMember.
  • Return less. Send the page only the fields it shows. Anything you return is visible in the browser.

3. Use it in page code

On the page, import the function from the backend and call it like any other async function.

import { getForecast } from "backend/weather.web";

$w.onReady(() => {
  $w("#searchButton").onClick(async () => {
    $w("#result").text = "Loading…";
    try {
      const forecast = await getForecast($w("#cityInput").value);
      $w("#result").text = `${forecast.summary}, ${forecast.temperature}°`;
    } catch (error) {
      $w("#result").text = "Couldn’t load the forecast. Please try again.";
    }
  });
});

The browser only ever talks to your Wix backend. The API key and the third-party request stay on the server.

When to go further

This pattern covers most integrations: CRMs, email tools, payment and shipping services, and AI APIs like OpenAI or Claude. As a project grows you might add caching so you don’t call the API on every page view, rate limits, or HTTP functions so the other service can send data back to your site through webhooks.

If you’d rather have this built for you, I do custom API integrations for Wix and AI integrations.