# Client setup

> Set up the HTTP client that calls Refabric: where it runs, where the key comes from, and what it must do itself.

You call the API with a plain HTTP client. This
page sets one up once, so every later example is a single call.

## Server, not browser

Your client runs on your server. A key is a secret that spends your credits, and the API refuses a
key sent from a browser — any request with an `Origin` header — with `403 api_key_not_allowed_here`,
on every operation. To call Refabric from a web or mobile app, send the app's requests to your own
server and call Refabric from there ([Proxy setup](https://docs.refabric.com/task-apis/calling-tasks/proxy-setup)).

## Install

Nothing beyond your language's HTTP client: `pip install requests` for Python; `fetch` is built into
Node.js 18 or later.

## Set the key

Read the key from the environment, never from source code:

```bash
export REFABRIC_API_KEY="<key>"
```

Use one variable name everywhere — `REFABRIC_API_KEY` — so examples and scripts find it the same
way ([Get your API key](https://docs.refabric.com/setting-up/get-your-api-key#create-a-key)).

## First call

A read that needs a key and spends no credits. A `200` with your balance means the setup works.

::::code-group
```python
import os, requests

API = "https://api.refabric.com/v1"
s = requests.Session()
s.headers["Authorization"] = f"Key {os.environ['REFABRIC_API_KEY']}"

r = s.get(f"{API}/account/balance", timeout=30)
r.raise_for_status()
print(r.json())
```

```javascript
const API = "https://api.refabric.com/v1";
const headers = { Authorization: `Key ${process.env.REFABRIC_API_KEY}` };

const r = await fetch(`${API}/account/balance`, { headers });
if (!r.ok) throw new Error(`${r.status} ${(await r.json()).error.code}`);
console.log(await r.json());
```

```bash
curl -s "https://api.refabric.com/v1/account/balance" -H "Authorization: Key $REFABRIC_API_KEY"
```
::::

## What your client must do

Build these once in your client:

| Concern | What to do | Where it is explained |
|---|---|---|
| Retries | retry when the error's `retryable` is `true`, with backoff; resend a submit with the same `Idempotency-Key` | [Task errors](https://docs.refabric.com/task-apis/errors/task-errors#retrying) |
| Rate limits | on `429`, wait `Retry-After` seconds | [Limits](https://docs.refabric.com/task-apis/limits#rate-limits) |
| Polling | read the job every few seconds at most; prefer a webhook for long jobs | [Asynchronous jobs](https://docs.refabric.com/task-apis/calling-tasks/asynchronous-jobs#read-a-job) |
| Waiting | with `Prefer: wait=N`, set the HTTP timeout longer than `N` (at most 60 seconds of waiting) | [Synchronous](https://docs.refabric.com/task-apis/calling-tasks/synchronous) |
| Pagination | follow `next_cursor` while `has_more` is `true` | [Conventions](https://docs.refabric.com/api-reference/platform/conventions#pagination) |
| Errors | branch on `error.code`, never on `message` | [Task errors](https://docs.refabric.com/task-apis/errors/task-errors) |

## Related

::::cards
:::card{title="JavaScript over HTTP" href="/api-reference/client-libraries/javascript-over-http"}
A complete `fetch` client: submit, poll, result, upload.
:::
:::card{title="Proxy setup" href="/task-apis/calling-tasks/proxy-setup"}
Call Refabric for a browser or mobile app.
:::
::::
