# API overview

Authentication, endpoint, errors, and rate limits.

## API overview

A single JSON-RPC endpoint authenticated with API tokens. Call any method by name.

> ℹ️ **No code needed?**
> <p>
>     The <a href="/guides/overview">Guides</a> set formbase up in Zapier click by click, without calling the API yourself.
>   </p>

<h2 id="endpoint">Endpoint</h2>
<p>
  All requests go to a single URL via <code>POST</code>. Pass the method name and parameters as JSON.
</p>

```
POST https://api.formbase.so/api/v1
```

<h2 id="auth">Authentication</h2>
<p>
  Pass your API token as a bearer token in the <code>Authorization</code> header. Tokens are scoped to a workspace — create them from{' '}
  <strong>OAuth and API Keys</strong> in the sidebar.
</p>

  
    
```
curl -X POST https://api.formbase.so/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{"method": "forms.list", "params": {}}'
```

  
  
    
```
const res = await fetch('https://api.formbase.so/api/v1', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FORMBASE_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ method: 'forms.list', params: {} }),
})
const data = await res.json()
```

  
  
    
```
import os, requests
res = requests.post(
    "https://api.formbase.so/api/v1",
    headers={"Authorization": f"Bearer {os.environ['FORMBASE_TOKEN']}"},
    json={"method": "forms.list", "params": {}},
)
data = res.json()
```

  

> ⚠️ **Keep tokens server-side**
> <p>Never embed tokens in browser code. Use a backend proxy for client-side calls.</p>

<h2 id="request-format">Request format</h2>
<p>Every request is a JSON object with two fields:</p>

```
{
  "method": "forms.list",
  "params": {
    "workspaceId": "abc123..."
  }
}
```

<h2 id="response-format">Response format</h2>
<p>
  Every response is a JSON object with an <code>ok</code> field. On success:
</p>

```
{
  "ok": true,
  "data": { ... }
}
```

<h2 id="errors">Errors</h2>
<p>
  On failure, <code>ok</code> is <code>false</code> and an <code>error</code> object contains a machine-readable code and human-readable
  message:
</p>

```
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Field 'name' is required."
  }
}
```

<p>Common error codes:</p>
<ul>
  <li>
    <code>VALIDATION_ERROR</code> (400) — invalid or missing parameters
  </li>
  <li>
    <code>UNAUTHORIZED</code> (401) — missing or invalid API token
  </li>
  <li>
    <code>UPGRADE_REQUIRED</code> (402) — the feature requires a higher subscription tier
  </li>
  <li>
    <code>FORBIDDEN</code> (403) — token lacks access to the resource
  </li>
  <li>
    <code>CONFLICT</code> (409) — resource state conflict, such as a duplicate name
  </li>
  <li>
    <code>NOT_FOUND</code> (404) — resource does not exist
  </li>
  <li>
    <code>METHOD_NOT_FOUND</code> (404) — unknown method name
  </li>
  <li>
    <code>RATE_LIMITED</code> (429) — too many requests
  </li>
  <li>
    <code>INTERNAL_ERROR</code> (500) — unexpected server error
  </li>
</ul>

<h2 id="rate-limits">Rate limits</h2>
<p>
  API requests are rate-limited to <strong>120 requests per minute</strong> per token. Exceeding the limit returns{' '}
  <code>429 Too Many Requests</code>.
</p>

<h2 id="next-steps">Next steps</h2>
<div class="not-prose grid gap-3 sm:grid-cols-2">
  - [API methods](/developers/rest-api) — Full list of available methods
  - [API tokens](/developers/api-tokens) — Create and manage tokens
  - [Webhooks reference](/developers/webhooks-reference) — Payload schema and signing
  - [MCP server](/developers/mcp-server) — Use formbase from AI agents
</div>
