# Your first request

A slower walkthrough than quickstart, explaining every part of the call. Use this page if the
quickstart worked but you want to know why.

## Anatomy of a request

| Part | Value | Why |
|---|---|---|
| Base URL | `https://api.scispace.com/v1` | |
| Auth header | `Authorization: Bearer …` | authentication |
| Version header | `SciSpace-Version: 2026-08-01` | versioning |
| Idempotency header | `Idempotency-Key: <uuid>` | safe retries on metered calls |
| Body | JSON | |

## Anatomy of a response

Every object comes back in the same envelope: an `object` type, a prefixed `id`, and `created_at`.

```json
{
  "object": "search",
  "id": "srch_9dm2pq4x1a",
  "created_at": "2026-08-11T09:14:22Z",
  "status": "succeeded",
  "depth": "standard",
  "paper_count": 20,
  "credits_cost": 5
}
```

Asynchronous creates return **`202 Accepted`**, not `200`, and the body has no result yet — only a
handle to poll or wait on:

```json
{
  "object": "search",
  "id": "srch_9dm2pq4x1a",
  "created_at": "2026-08-11T09:14:22Z",
  "status": "queued",
  "answer": null,
  "completed_at": null,
  "error": null
}
```

`status` moves `queued → running → succeeded | failed | canceled`. Only `succeeded` populates the
result; `failed` populates `error`. See Job.

## Response headers

These come back on every response, including errors. This is the only page that explains them —
reference pages link here.

| Header | Example | What to do with it |
|---|---|---|
| `X-Request-Id` | `req_5a0c1e8b7d` | Log it. Quote it in every support ticket; it is how we find your call. |
| `X-RateLimit-Limit` | `60` | Requests allowed in the current window, for this key. |
| `X-RateLimit-Remaining` | `57` | Requests left. Slow down as it approaches zero. |
| `X-RateLimit-Reset` | `1786518900` | Unix seconds when the window resets. |
| `X-Credits-Cost` | `5` | Credits this call consumed. `0` on reads and on errors. |
| `X-Credits-Remaining` | `9,412` | Balance after the call. Alert on it; do not wait for a `402`. |

On `429`, `Retry-After` is also sent, in seconds. Honour it rather than guessing — see
errors-and-retries and rate-limits.

## Making it async-safe

Searches, chats, documents, extractions and topic searches are asynchronous. Three ways to wait, in
descending order of preference.

**Webhooks — best for servers.** Register once, get pushed the terminal state. No polling, no
timeout to tune.

```bash
curl https://api.scispace.com/v1/webhook_endpoints \
  -H "Authorization: Bearer $SCISPACE_API_KEY" \
  -H "SciSpace-Version: 2026-08-01" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/scispace",
    "enabled_events": ["search.succeeded", "search.failed"]
  }'
```

See webhook-endpoints for signature verification — verify before you trust the payload.

**`.wait()` — best for scripts.** The SDKs poll for you with the right backoff.

```python
search = client.searches.create(
    query="How does climate change affect biodiversity?",
    depth="standard",
).wait(timeout=60)
```

```typescript
const search = await client.searches
  .create({ query: "How does climate change affect biodiversity?", depth: "standard" })
  .wait({ timeoutMs: 60_000 });
```

**Polling — when you have neither.** Fetch the resource itself; there is no separate status endpoint.
Start at 1s, double to a 30s ceiling, and give up at the depth's p95 latency plus headroom.

```bash
delay=1
while :; do
  status=$(curl -sS "https://api.scispace.com/v1/searches/srch_9dm2pq4x1a" \
    -H "Authorization: Bearer $SCISPACE_API_KEY" \
    -H "SciSpace-Version: 2026-08-01" | jq -r .status)
  [ "$status" = "succeeded" ] && break
  [ "$status" = "failed" ] && exit 1
  sleep "$delay"
  delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))
done
```

> [!warning] Do not poll in a tight loop
> Every poll is a request against your rate limit. Retrieves cost no credits, but a 100ms loop will
> exhaust the limit long before a `deep_review` finishes.

## Related

conventions · job · errors-and-retries
