Get started
Search lifecycle
A search is asynchronous. You submit it, it runs against the official source, and you read the result when it finishes.
The flow
POST /api/v1/search -> 202 { id, status: "queued" }
GET /api/v1/search/{id} -> status: queued -> running -> succeeded | failed | cancelled
GET /api/v1/search/{id}/documents/{filename} -> 302 to a short-lived signed URLSubmit
POST /api/v1/search takes one search, or up to 25 in searches[]. The charge is taken at submit. A request that fails validation returns 422 and charges nothing.
{
"state": "telangana",
"search_type": "ec",
"params": { "...": "see the search page for its fields" },
"label": "Case 8841 EC",
"callback_url": "https://your-app.example.com/hooks/legiscore"
}{
"searches": [
{ "state": "andhra", "search_type": "prohibited", "params": { "...": "..." } },
{ "state": "karnataka", "search_type": "ec", "params": { "...": "..." } }
],
"callback_url": "https://your-app.example.com/hooks/legiscore"
}In a batch, each item is accepted or refused on its own. Refused items come back in rejected[] as { index, code, message, retry_after? } and are not charged.
Statuses
| status | Meaning | Terminal |
|---|---|---|
queued | Accepted and charged. Waiting for a free slot. | No |
running | The source is being queried. progress is an estimate from 0 to 100. | No |
awaiting_otp | The source asked for a one-time code. See OTP below. | No |
succeeded | Finished. found, records and documents are filled in. | Yes |
failed | The source could not answer. The charge is refunded. error says why. | Yes |
cancelled | You cancelled it. Refunded when the service confirms. | Yes |
A search that is still not terminal after 3 hours is failed and refunded automatically. Poll every 5 to 10 seconds, or use a webhook.
Reading the result
| Field | Meaning |
|---|---|
found | null until the search finishes. false means the source answered and holds nothing under that query. |
records[] | The rows the source returned. Their shape depends on the search. |
documents[] | { filename, content_type, bytes, withheld }. Download each one with the documents route. |
note | A coverage caveat from the source, when there is one. |
error | { kind, message } on a failed search. |
refunded | Whether this search was refunded. |
found: false means no per-property match was computed, not that the property is clear. Read the documents.Failure kinds
| error.kind | Retry? |
|---|---|
portal_blocked | Yes, later. The source refused or was unreachable. |
portal_timeout | Yes. The source was too slow. |
portal_changed | Not until we fix it. Report it to us. |
captcha_failed | Yes. |
otp_timeout | Yes. |
invalid_input | No. Correct the parameters first. |
internal | Yes, then contact us if it repeats. |
Documents
GET /api/v1/search/{id}/documents/{filename} answers 302 to a signed URL valid for one hour. Request it again for a fresh link. If a file failed its integrity check we withhold it (410 withheld) rather than hand you bad bytes.
OTP
Some sources verify with a one-time code (searches marked OTP on their page). LegiScore handles the code for you, so the search may sit in awaiting_otp for a short time and then continue. You do not need to do anything. If a search needs a code sent to your registered mobile, send it with:
POST /api/v1/search/{id}/otp
{ "otp": "123456" }The code is 4 to 8 digits. Sending it to a search that is not waiting returns 409 not_waiting.
Cancel
DELETE /api/v1/search/{id} cancels a search that has not finished. On a finished search you get 409 already_finished.
Idempotency and repeats
| Situation | Cost | Result |
|---|---|---|
| Same Idempotency-Key within 24 hours | Free | Original search returned, header Idempotent-Replayed: true, HTTP 200 |
| Same search again by the same person within 24 hours | Free | Same id, replayed: true |
| Same search by a colleague in the same workspace within 24 hours | Charged | Served from the recent result with a new id |
Send Idempotency-Key as 1 to 255 printable ASCII characters, no spaces, for example a case id plus the search type. Reusing a key with a different body returns 409 idempotency_key_reused.
List your searches
GET /api/v1/search?limit=50&offset=0&status=succeeded&batch_id=... returns your searches newest first. limit is at most 200.