> ## Documentation Index
> Fetch the complete documentation index at: https://docs.extensy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding APIs

> Call external REST APIs from your Extensy-generated extension using fetch() in content scripts or the background service worker.

Extensy-generated extensions can call any public REST API directly from the browser. You write the prompt, Extensy generates the `fetch()` calls — but understanding where those calls live and how to handle API keys gives you full control over the result.

> [!NOTE]
>
> Extensy handles client-side API calls only. There is no server-side backend — all requests originate from the user's browser.

## Choose where to make the API call

Place your `fetch()` call in the right file depending on what triggers it:

| File | When to use |
| - | - |
| `content.js` | The API call is triggered by user interaction on a page, or needs access to page content |
| `background.js` | The API call is cross-origin, scheduled (alarm-based), or independent of any specific page |

> [!WARNING]
>
> Direct `fetch()` calls from `content.js` may be blocked by a website's Content Security Policy (CSP). If a site sets a strict CSP, move the request to `background.js` and use message passing to send the result back to the content script.

## Call an API from a content script

Use this pattern when the request is triggered by something the user does on a page:

```javascript theme={null}
// content.js
async function fetchWeather(city) {
  const apiKey = "YOUR_API_KEY";
  const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${apiKey}&units=metric`;

  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const data = await response.json();
  return data;
}

// Call it when the user clicks a button injected into the page
document.getElementById("check-weather-btn").addEventListener("click", async () => {
  const weather = await fetchWeather("London");
  console.log(`Temperature: ${weather.main.temp}°C`);
});
```

## Call an API from the background service worker

Use this pattern for cross-origin requests or calls that run independently of any tab:

```javascript theme={null}
// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === "FETCH_WEATHER") {
    fetchWeather(message.city).then(sendResponse);
    return true; // Keep the message channel open for async response
  }
});

async function fetchWeather(city) {
  const apiKey = "YOUR_API_KEY";
  const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${apiKey}&units=metric`;

  const response = await fetch(url);
  const data = await response.json();
  return data;
}
```

Then send the message from your content script or popup:

```javascript theme={null}
// content.js or popup.js
const weather = await chrome.runtime.sendMessage({
  type: "FETCH_WEATHER",
  city: "London"
});
console.log(weather.main.temp);
```

## Store API keys securely

Never hardcode sensitive API keys into your extension if you plan to distribute it — anyone can unpack a `.crx` file and read them.

**Options page (recommended)**

Let users enter their own API key and save it with `chrome.storage.local`:

```javascript theme={null}
// options.js — save the key
document.getElementById("save-btn").addEventListener("click", () => {
  const key = document.getElementById("api-key-input").value.trim();
  chrome.storage.local.set({ openweatherApiKey: key }, () => {
    console.log("API key saved.");
  });
});
```

Then read it wherever you make the fetch call:

```javascript theme={null}
// background.js — read the key before fetching
chrome.storage.local.get(["openweatherApiKey"], ({ openweatherApiKey }) => {
  if (!openweatherApiKey) {
    console.warn("No API key set. Visit the options page.");
    return;
  }
  fetchWeather("London", openweatherApiKey);
});
```

**Hardcoded (personal use only)**

If the extension is for personal use only and will never be published, you can hardcode the key directly:

```javascript theme={null}
const API_KEY = "sk-abc123yourkeyhere";
```

> [!WARNING]
>
> Hardcoded keys are visible to anyone who inspects the extension package. Do not publish an extension with hardcoded API credentials to the Chrome Web Store.

## Prompt Extensy to add an API call

When generating your extension, describe the API call in your prompt:

> "When the user clicks the toolbar icon, fetch the current Bitcoin price from `https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd` and display it in the popup."

Extensy will generate the `fetch()` call in the appropriate file and wire up the response to the UI. If the API requires a key, specify where you want it stored:

> "Store the API key in `chrome.storage.local` and let users enter it on the options page."

## Handle errors gracefully

Always check `response.ok` and handle network failures:

```javascript theme={null}
async function callApi(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Request failed with status ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error("API call failed:", error.message);
    return null;
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.