> For the complete documentation index, see [llms.txt](https://docs.r.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.r.xyz/glider-api/individuals-or-pay-as-you-go/api-documentation.md).

# API Documentation

#### Supported Blockchains <a href="#supported-blockchains" id="supported-blockchains"></a>

**Endpoint:** GET /api/v1/chain/list

**Description:** Retrieve a list of all supported blockchains for contract analysis. Use the `chainid` value from the response when analyzing contracts.

### Endpoint

````
| | |
|---|---|
| **Method** | `POST` |
| **Path** | `/api/v1/portal/contract/analyze-risk` (Swagger advertises `/v1/portal/contract/analyze-risk`; the live base path is `/api`) |
| **Content-Type** | `application/json` |
| **Auth** | API key in body (`api_key`). No header-based auth. |
| **Idempotency** | Per `(user_id, address, chain_id)`. Re-calls return the cached result without a new charge. |
| **Async** | First call for a fresh contract returns **202 pending**. Client polls the same endpoint until **200 ready**. |

## Request body

```json
{
  "api_key":  "string",
  "address":  "string",
  "chain_id": "string"
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `api_key` | string | yes | Risk-API scoped access token issued in the portal. |
| `address` | string | yes | Contract address. EVM addresses must be `0x`-prefixed; non-EVM follows the chain's native format. |
| `chain_id` | string | yes | Decimal string (e.g. `"1"`, `"8453"`, `"56"`). |

Empty or malformed JSON, or any missing field, → **400 `INVALID_REQUEST_PAYLOAD`**.
````

### Successful responses

````
Both 200 and 202 share a single envelope shape. Branch on the `status` field — only the matching subset of fields is populated.

### 200 OK — `status: "ready"`

The analysis is cached; result is returned inline. The user has been charged for this `(address, chain_id)` exactly once across the lifetime of the API key (subsequent ready-hits are free).

```json
{
  "status":     "ready",
  "address":    "0xdac17f958d2ee523a2206206994597c13d831ec7",
  "chain_id":   "1",
  "query_name": "token-risk",
  "results":    { /* analyzer output, shape depends on query */ }
}
```

| Field | Type | Notes |
|---|---|---|
| `status` | enum | `"ready"` for 200. |
| `address` | string | Echoed from request. |
| `chain_id` | string | Echoed from request. |
| `query_name` | string | Identifier of the analyzer query that produced `results`. |
| `results` | object/array | Free-shape analyzer payload. Stable per `query_name`/`query_version` upstream. |

### 202 Accepted — `status: "pending"`

The contract has not been analyzed yet. `gld-service-data-analyze` enqueues a background task and returns immediately. Client should retry after `retry_after_seconds`.

```json
{
  "status":              "pending",
  "retry_after_seconds": 30
}
```

| Field | Type | Notes |
|---|---|---|
| `status` | enum | `"pending"` for 202. |
| `retry_after_seconds` | int | Currently `30`. Client should treat as advisory (lower bound). |

A pending response is **not billed**. Billing happens on the first successful 200.
````

### Error responses

````
| HTTP | `error_code` | When |
|---|---|---|
| `400` | `INVALID_REQUEST_PAYLOAD` | Body is not JSON / required fields missing / fails struct validation. |
| `400` | `INVALID_ADDRESS_FORMAT` | Address fails chain-specific format check (passthrough from data-analyze). |
| `400` | `INVALID_QUERY_NAME` | Resolved query name is not registered (configuration issue, normally not surfaced). |
| `400` | `ANALYSIS_FAILED` | Background analysis terminally failed (`ANALYSIS_FAILED`, `ANALYSIS_TIMEOUT`, `ANALYSIS_CANCELED` from data-analyze are all surfaced under this code). |
| `400` | `INTERNAL_ERROR` | Catch-all for unclassified errors. |
| `401`/`404` | — | Returned when the supplied `api_key` is not found / not in `risk-api` scope. Status code is the verbatim passthrough from `gld-service-user`. |
| `402` | `INSUFFICIENT_FUNDS` | Billing balance is below the per-call price. User must top up. |
| `429` | `RATE_LIMIT_EXCEEDED` | Per-key rate limit exceeded (passthrough from data-analyze). |

### Error body

```json
{
  "error_code": "INSUFFICIENT_FUNDS",
  "message":    "insufficient funds"
}
```

For payload-shape errors only `error_code` is guaranteed; for validator failures the body additionally includes `error` with the validator string. Clients should branch on `error_code` and treat `message`/`error` as human-readable detail.
````

### Examples

````
### cURL — fresh request (202 → poll → 200)

```bash
curl -sS -X POST  \
  -H 'Content-Type: application/json' \
  -d '{"api_key":"sk_live_…","address":"0xdac17f958d2ee523a2206206994597c13d831ec7","chain_id":"1"}'
# → 202 {"status":"pending","retry_after_seconds":30}

# wait 30s, retry the same request
# → 200 {"status":"ready","address":"…","chain_id":"1","query_name":"token-risk","results":{…}}
```
````

### Polling pseudocode

````
```python
while True:
    r = post(url, json=body)
    if r.status_code == 200:
        return r.json()["results"]
    if r.status_code == 202:
        sleep(r.json().get("retry_after_seconds", 30))
        continue
    raise ApiError(r.status_code, r.json())
````

### Behavior notes

* **Pending is free.** Polling does not deduct from the user's balance. Only the first 200 response charges.
