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

# API

> Build prospect lists, enrich them with verified emails, and read the results programmatically with the Hizura API.

## Overview

The API is how you run Hizura from your own stack — building lists on a schedule, wiring them into your own systems, or enriching records you already hold.

<Note>
  **API keys are issued to Enterprise accounts.** On every other plan, use the [MCP server](/mcp) — it authenticates as you over OAuth, needs no key, and exposes the same research engine.
</Note>

## Authentication

Authenticate every request with your key in the `x-api-key` header.

```bash theme={null}
export HIZURA_BASE_URL="https://api.hizura.com"
export HIZURA_API_KEY="<API_KEY>"

curl "$HIZURA_BASE_URL/tables" \
  -H "x-api-key: $HIZURA_API_KEY"
```

<Warning>
  Keep API keys server-side. Never expose them in browser code, public repositories, or logs.
</Warning>

## Build a list

`POST /tables/stream` is the endpoint you'll use most. Send your request as `query` and Hizura researches it, streaming progress as newline-delimited JSON (`application/x-ndjson`).

```bash theme={null}
curl "$HIZURA_BASE_URL/tables/stream" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-api-key: $HIZURA_API_KEY" \
  -d '{
    "name": "EU fintech ICP",
    "query": "Find 25 B2B fintech companies.\n\nCriteria:\n1. Headquartered in the EU (compulsory)\n2. Preferably Series A or later (optional)\n\nFor each company, extract:\n- Website\n- Headquarters\n- Employee count\n- LinkedIn company page(link)"
  }'
```

One JSON object per line, ending in exactly one `result` or `error`:

```jsonl theme={null}
{"type":"progress","stage":"planning","message":"Planned the research — 4 columns to fill…"}
{"type":"progress","stage":"discovery","message":"Searching the web: “EU B2B fintech companies”"}
{"type":"progress","stage":"extraction","message":"Extracted 18/25 companies…","detail":{"completed":18,"total":25}}
{"type":"result","data":{"table":{"id":123,"name":"EU fintech ICP","rows":[]}}}
```

Write `query` the way you'd brief a person — there's no interview in front of the API, so name every column you want and any criteria that matter. It accepts up to 8,000 characters.

<Tip>
  Prefer `POST /tables` if you'd rather block until the run completes — same body, one JSON response, no stream to parse. Runs take several minutes, so set a long client timeout.
</Tip>

You can also create an empty table to fill yourself, which costs no credits:

```bash theme={null}
curl "$HIZURA_BASE_URL/tables" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-api-key: $HIZURA_API_KEY" \
  -d '{ "name": "Inbound leads Q3" }'
```

### Recovering a run

A run continues server-side even if your client disconnects, so **don't retry the request** — a second generation while one is in flight returns `409`. Poll instead:

```bash theme={null}
curl "$HIZURA_BASE_URL/tables/generation-status" \
  -H "x-api-key: $HIZURA_API_KEY"
```

```json theme={null}
{
  "processing": false,
  "lastRunStatus": "succeeded",
  "lastRunTableId": 123,
  "lastRunError": null
}
```

`processing` tells you whether a run is live; `lastRunStatus` distinguishes a finished run from a failed one; `lastRunTableId` is the table it produced. While `processing` is true, `progressLog` carries the steps completed so far.

Once streaming has begun, the HTTP status stays `200` and failures arrive in-band so partial progress survives:

```json theme={null}
{"type":"error","status":502,"error":"AI service request failed. Please try again."}
```

## Add verified emails

`POST /tables/{tableId}/enrich-emails` finds verified corporate emails for rows you already have. Requires edit access and a paid plan, takes up to 500 `rowIds`, and streams NDJSON.

```bash theme={null}
curl "$HIZURA_BASE_URL/tables/123/enrich-emails" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-api-key: $HIZURA_API_KEY" \
  -d '{ "rowIds": [12, 13, 14] }'
```

```jsonl theme={null}
{"type":"row_result","row_id":12,"found":true,"cell":{"email":"ada@acme.ai","verified":true}}
{"type":"row_result","row_id":13,"found":false,"reason":"no_company_domain"}
{"type":"result","data":{"run_summary":{}}}
```

You're billed only for rows where `found` is `true`. When it isn't, `reason` is one of `insufficient_credits`, `unparseable_name`, `no_company_domain`, `no_deliverable_address`, or `lookup_failed`.

## Read the results

```bash theme={null}
# List your tables
curl "$HIZURA_BASE_URL/tables" -H "x-api-key: $HIZURA_API_KEY"

# One table, with its rows
curl "$HIZURA_BASE_URL/tables/123" -H "x-api-key: $HIZURA_API_KEY"

# Just the rows
curl "$HIZURA_BASE_URL/tables/123/rows" -H "x-api-key: $HIZURA_API_KEY"
```

A table response carries its metadata, rows, owner summary, and your access level. Row values live in each row's `data` object, keyed by column name. Researched values arrive as `{ "result": …, "source": [{ "url": … }] }`; link and email columns have their own shapes.

Filter rows on one field with an exact match — useful for pulling a single segment out of a larger table:

```bash theme={null}
curl "$HIZURA_BASE_URL/tables/123/rows?Headquarters=Berlin%2C%20Germany" \
  -H "x-api-key: $HIZURA_API_KEY"
```

## Write back

Rows hold flexible JSON in `data`, so you can push your own records in and update them as deals progress.

```bash theme={null}
# Add a row
curl "$HIZURA_BASE_URL/tables/123/rows" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-api-key: $HIZURA_API_KEY" \
  -d '{ "tableId": 123, "data": { "Company": "Acme", "Website": "https://acme.ai" } }'

# Update one — PATCH merges into the existing row
curl "$HIZURA_BASE_URL/tables/123/rows/456" \
  -X PATCH \
  -H "Content-Type: application/json" \
  -H "x-api-key: $HIZURA_API_KEY" \
  -d '{ "data": { "Stage": "Contacted" } }'

# Delete one
curl "$HIZURA_BASE_URL/tables/123/rows/456" \
  -X DELETE -H "x-api-key: $HIZURA_API_KEY"
```

<Note>
  Include `tableId` in the body as well as the path when adding a row. The path value is what selects the table.
</Note>

Tables themselves take `PATCH /tables/{tableId}` for `name` and `description`, and `DELETE /tables/{tableId}` if you own them.

## Access and limits

* Your key acts as your account: you can read tables you own or that are shared with you, write where you have edit access, and delete only what you own.
* **One run at a time** per account. Generating while a run is in flight returns `409`.
* Runs are capped at 30 minutes.
* Generating tables and finding emails spends credits unless you're on Enterprise, which isn't metered. See [Credits](/concepts/credits).
* Sharing, collaborator management, and API-key management are session-authenticated — do those in the app.

## Errors

| Status | When it happens                                          |
| ------ | -------------------------------------------------------- |
| `400`  | Required fields missing, or invalid JSON                 |
| `401`  | `x-api-key` missing or invalid                           |
| `403`  | Your account lacks the required access, plan, or credits |
| `404`  | The table or row isn't available to your account         |
| `409`  | A table is already being generated for your account      |
| `502`  | The upstream research service failed                     |
| `504`  | The run exceeded its 30-minute cap                       |
