Call the API in 5 minutes
Create an API key, check who you are, and list and create leads with curl, Python or TypeScript.
Create an API key
In the ZeroReach app, open Settings → API keys and create a key with the scopes you need. For this quickstart: leads:read and leads:write (or leads:*, which grants both).
The key looks like ro_pat_ followed by 43 characters. It is shown once: only a hash is stored, so copy it now.
Keys are created by a signed-in person. An API key can't create or revoke keys (POST /api/v1/workspaces/current/api-keys refuses API keys).
Set two environment variables
export ZEROREACH_URL="http://localhost:8000" # or your ZeroReach host
export ZEROREACH_API_KEY="ro_pat_..." # the key you just createdCheck who you are
GET /api/v1/me works with any valid key, whatever its scopes. The key implies its workspace, so you don't need an X-Workspace-Id header.
curl -s "$ZEROREACH_URL/api/v1/me" \
-H "Authorization: Bearer $ZEROREACH_API_KEY"import os
import httpx
client = httpx.Client(
base_url=os.environ["ZEROREACH_URL"],
headers={"Authorization": f"Bearer {os.environ['ZEROREACH_API_KEY']}"},
)
me = client.get("/api/v1/me")
me.raise_for_status()
print(me.json()["user"])const base = process.env.ZEROREACH_URL!;
const headers = { Authorization: `Bearer ${process.env.ZEROREACH_API_KEY}` };
const res = await fetch(`${base}/api/v1/me`, { headers });
if (!res.ok) throw new Error(`${res.status}: ${JSON.stringify(await res.json())}`);
const me = await res.json();
console.log(me.user);List leads
Lists are newest first and paged with a cursor: pass next_cursor back as cursor until it is null.
curl -s "$ZEROREACH_URL/api/v1/leads?limit=25" \
-H "Authorization: Bearer $ZEROREACH_API_KEY"cursor = None
while True:
params = {"limit": 100, **({"cursor": cursor} if cursor else {})}
page = client.get("/api/v1/leads", params=params).json()
for lead in page["items"]:
print(lead["id"], lead.get("email"))
cursor = page["next_cursor"]
if cursor is None:
breaklet cursor: string | null = null;
do {
const url = new URL(`${base}/api/v1/leads`);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const page = await (await fetch(url, { headers })).json();
for (const lead of page.items) console.log(lead.id, lead.email);
cursor = page.next_cursor;
} while (cursor);Create a lead
Leads are deduplicated on email within a workspace: creating a second live lead with the same email answers 409 with the existing lead's id (existing_id).
curl -s -X POST "$ZEROREACH_URL/api/v1/leads" \
-H "Authorization: Bearer $ZEROREACH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"first_name": "Ana", "last_name": "Silva", "email": "ana@example.com", "timezone": "America/New_York"}'lead = client.post(
"/api/v1/leads",
json={"first_name": "Ana", "last_name": "Silva", "email": "ana@example.com"},
)
if lead.status_code == 409:
print("Already exists:", lead.json()["detail"])
else:
lead.raise_for_status()
print(lead.json()["id"])const created = await fetch(`${base}/api/v1/leads`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ first_name: "Ana", last_name: "Silva", email: "ana@example.com" }),
});
console.log(created.status, await created.json());What can go wrong
| Status | Meaning |
|---|---|
401 | Invalid or expired API key. The key is wrong, revoked or expired. |
403 | This API key lacks the '<scope>' scope., or a route API keys can't use. |
409 | A conflict, e.g. a duplicate lead. |
422 | The request didn't validate; detail lists each problem. |
429 | Too many requests for this key (default 600 per minute) or too many failed key attempts from your IP. Wait for Retry-After seconds. |
Next: Authentication, API basics and the API reference.