LLegiScoreSearch API
Get an API key
Get started
IntroductionAuthenticationQuickstartSearch lifecycleErrorsWebhooksCreditsGet an API key
API reference
Credit balancegetCataloggetLookupsgetSearchespost
States
All states
View as Markdownllms-full.txt

Get started

Quickstart

Run your first search in three calls: check your balance, submit, then fetch the result.

Before you start

You need an API key (how to get one) and credits in your search wallet. Credits are charged per search (Maharashtra Index-II by property: per year searched, 20 credits a year). A failed search is refunded.

Copy setup instructions

Using Claude, Cursor, ChatGPT or another coding agent? Paste this prompt first. It explains the base URL, the key, the submit, poll and result flow, the lookup rule and refunds, and points to the full docs.

Prompt for your AI agent
You are integrating the LegiScore Search API: government land and property record searches across Indian states.

Full documentation (read it before writing code): https://legiscore.in/docs
Machine-readable: https://legiscore.in/docs/llms-full.txt (all pages) and https://legiscore.in/docs/llms.txt (index). Any page as Markdown: https://legiscore.in/docs/md/<path>.

BASE URL
https://legiscore.in/api/v1

AUTHENTICATION
- Send "Authorization: Bearer <key>" (or "X-API-Key: <key>") on every request. Keys start with "lsk_".
- Read the key from the environment variable LEGISCORE_API_KEY. Never hardcode it, never log it, never ship it to a browser or mobile app.
- Only GET /api/v1/search/catalog works without a key.

FLOW: SUBMIT, POLL, RESULT
1. Find the search in the catalog: GET /api/v1/search/catalog?state=<state>. It lists each search_type, its fields, and whether it is live or held.
2. Submit: POST /api/v1/search with {"state", "search_type", "params", "label"?, "callback_url"?}. Add an Idempotency-Key header so a retry never double-charges. You get 202 and a search id (SRCH-...).
3. Poll GET /api/v1/search/{id} every 5 to 10 seconds, or pass an https callback_url and verify the X-LegiScore-Signature HMAC header. Stop when status is succeeded, failed or cancelled.
4. Read found, records and documents. Download a document with GET /api/v1/search/{id}/documents/{filename} (302 to a one-hour signed URL).
- found:false on a search marked "Register" does NOT mean the property is clear. Read the documents.
- Some searches use an OTP. LegiScore handles it; the status may show awaiting_otp for a short time. Do nothing.

LOOKUPS: SEARCH BY NAME, THEN SEARCH
- Where a field needs a place or office code, find it by NAME with GET /api/v1/search/lookups?state=<state>&dim=<lookup type>&q=<name>. The lookup type is named on each search page.
- Send q (2 characters or more). At most 10 matches return. Rate limit: 120 lookups a minute per key.
- Take the code from the chosen match's "meta" and pass it in params.
- NEVER try to list everything in a lookup, page through it, or loop over letters to enumerate it. That is not supported and will be refused or rate limited.

ERRORS AND REFUNDS
- Every error is {"error": {"code", "message"}}. 422 invalid_params and 429 follow RFC 9457; read invalid_params[] and fix the named field. A 422 is never charged.
- 429: wait Retry-After seconds, then retry. 402 insufficient_credits: stop and tell the user to top up.
- A search that fails or is cancelled is refunded automatically. A search that is HELD is refused with 422 search_unavailable before any charge.
- Check GET /api/v1/search/credits for the wallet balance.

When unsure about a search's parameters, fetch its page: https://legiscore.in/docs/states/<state>/<search-type-with-dashes>.

1. Check your balance

curl "https://legiscore.in/api/v1/search/credits" \
  -H "Authorization: Bearer lsk_your_api_key"
import requests

resp = requests.get(
    "https://legiscore.in/api/v1/search/credits",
    headers={"Authorization": "Bearer lsk_your_api_key"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://legiscore.in/api/v1/search/credits", {
  method: "GET",
  headers: { Authorization: "Bearer lsk_your_api_key" },
});
if (!resp.ok) throw new Error(await resp.text());
console.log(await resp.json());
json
{
  "data": {
    "scope": "organization",
    "balance": 1200,
    "spendable": 1200,
    "postpaid": { "enabled": false, "limit": 0 },
    "ledger": []
  }
}

2. Submit a search

This one looks up a housing project on the Andhra Pradesh RERA register by name. Searches that need a place or an office first resolve it with a lookup; this one takes plain text.

curl -X POST "https://legiscore.in/api/v1/search" \
  -H "Authorization: Bearer lsk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "andhra",
  "search_type": "rera",
  "params": {
    "search_term": "Aparna Amaravati One"
  },
  "label": "Quickstart test"
}'
import requests

resp = requests.post(
    "https://legiscore.in/api/v1/search",
    headers={"Authorization": "Bearer lsk_your_api_key"},
    json={
      "state": "andhra",
      "search_type": "rera",
      "params": {
        "search_term": "Aparna Amaravati One"
      },
      "label": "Quickstart test"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://legiscore.in/api/v1/search", {
  method: "POST",
  headers: { Authorization: "Bearer lsk_your_api_key", "Content-Type": "application/json" },
  body: JSON.stringify({
    "state": "andhra",
    "search_type": "rera",
    "params": {
      "search_term": "Aparna Amaravati One"
    },
    "label": "Quickstart test"
  }),
});
if (!resp.ok) throw new Error(await resp.text());
console.log(await resp.json());

You get 202 Accepted and a search object. Keep the id:

json
{
  "data": {
    "searches": [
      {
        "id": "SRCH-2026-A1B2C3D4",
        "state": "andhra",
        "search_type": "rera",
        "status": "queued",
        "progress": 0,
        "found": null,
        "documents": [],
        "refunded": false
      }
    ],
    "rejected": []
  }
}

3. Fetch the result

Poll every 5 to 10 seconds until status is succeeded, failed or cancelled. Or pass a callback_url when you submit and skip polling (webhooks).

curl "https://legiscore.in/api/v1/search/SRCH-2026-A1B2C3D4" \
  -H "Authorization: Bearer lsk_your_api_key"
import requests

resp = requests.get(
    "https://legiscore.in/api/v1/search/SRCH-2026-A1B2C3D4",
    headers={"Authorization": "Bearer lsk_your_api_key"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://legiscore.in/api/v1/search/SRCH-2026-A1B2C3D4", {
  method: "GET",
  headers: { Authorization: "Bearer lsk_your_api_key" },
});
if (!resp.ok) throw new Error(await resp.text());
console.log(await resp.json());
Retry safely
Add an Idempotency-Key header to the submit call. A retry with the same key within 24 hours returns the original search and charges nothing.
PreviousAuthenticationNextSearch lifecycle