# iIndexer API documentation

---

Call iIndexer programmatically with your own API key. Base URL: `https://api.iindexer.com`

---

## Quickstart

Three steps from zero to your first indexed URL.

```bash
# 1. Create a key from your Profile page (shown once - save it right away)

# 2. Submit a URL for indexing
curl -X POST "https://api.iindexer.com/tasks" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/page", "idempotencyKey": "quickstart-1"}'

# 3. Check its status
curl "https://api.iindexer.com/tasks?status=DONE" \
  -H "X-API-Key: YOUR_API_KEY"
```

---

## Authentication

Send your API key in the `X-API-Key` header on every request. Keys look like `cix_<random string>`.

```bash
curl "https://api.iindexer.com/auth/me" \
  -H "X-API-Key: YOUR_API_KEY"
```

Don't have a key yet? Create one from your Profile page in the iIndexer dashboard - it's shown once, so save it right away.

---

## Errors

Every error response has the same shape - `code` is a stable machine-readable slug you can safely branch on; `message` is human-readable and may change wording over time. Build your logic against `code`, never against `message` text.

```json
{
  "statusCode": 402,
  "code": "insufficient_balance",
  "message": "User \"usr_123\" has insufficient balance: has 0.500000, needs 2.000000"
}
```

### Status codes used by this API

| Field | Type | Notes |
| --- | --- | --- |
| `400` | `bad_request / unsafe_url / bulk_submit_limit_exceeded` | Request body/query failed validation, a /url-checks/* URL was blocked (private IP, invalid scheme), or a POST /tasks/bulk request exceeded your account's bulk submit limit. |
| `401` | `unauthorized` | Missing or invalid X-API-Key. |
| `402` | `insufficient_balance` | Your balance can't cover this task's cost - top up and retry. |
| `404` | `not_found` | The resource (e.g. a specific task) doesn't exist or isn't yours. |
| `422` | `deposit_verification_failed` | Only relevant to the payments flow, not the endpoints on this page. |
| `429` | `rate_limited` | Too many requests - see Rate limits below. |
| `500` | `internal_error / pricing_plan_not_found` | Something failed on our side. Safe to retry. |
| `503` | `no_provider_available / maintenance_mode` | Indexing is temporarily unavailable, or the system is in scheduled maintenance - retry shortly. |

---

## Rate limits

300 requests per 60 seconds by default, tracked per API key (or per JWT session on the dashboard) - not per IP, so other users sharing your network (NAT, office, shared server) never eat into your quota. Some account endpoints (login, registration, password reset) are limited more strictly to prevent abuse.

Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. Exceeding the limit returns `429` with `code: "rate_limited"` - back off and retry after the window resets.

---

## Pagination

Every list endpoint below shares the same query parameters and response shape.

| Field | Type | Notes |
| --- | --- | --- |
| `page` | `number?` | Default 1. |
| `pageSize` | `number?` | Default 20, max 100. |

Responses look like `{ "items": [...], "total": number, "page": number, "pageSize": number }`.

---

## Speed tiers

Every task picks a `speedTier` at submit time - `"fast"` (default) or `"instant"`. This controls how quickly your task is dispatched for indexing AND how long we wait before automatically refunding an unconfirmed task.

| Field | Type | Notes |
| --- | --- | --- |
| `fast (default)` | `1x pricePerUrl` | Submitted at standard queue priority. Auto-refunds if not indexed within 7 days. |
| `instant` | `5x pricePerUrl` | Jumps ahead of fast jobs in the queue. Auto-refunds if not indexed within 3 days. |

"Instant" only speeds up dispatch from our own submission queue - we can't make Google itself index faster. What you're really paying for is priority queueing plus a shorter refund window if indexing isn't confirmed in time.

On top of either tier, `dripFeed: true` is a free add-on (default false) - spreads submission out at a random time within ~6 hours instead of right away, and adds +7 days to that tier's auto-refund window (fast: 7→14 days, instant: 3→10 days). It is not a 3rd tier - it's combinable with either `speedTier` above.

---

## Task lifecycle

`PENDING` -> `SUBMITTED` -> `PROCESSING` -> `DONE` | `FAILED`

Your balance is charged immediately when you submit the task - not when Google finishes indexing. Submission itself is asynchronous: `POST /tasks` returns `PENDING` right away, and dispatch through our indexing pipeline happens from a priority queue shortly after (see Speed tiers above). If dispatch isn't possible, the task goes straight to `FAILED` and you're refunded automatically.

We verify indexing independently (a real Google search check, not just taking our own dispatch confirmation as proof) once a day. If it's not confirmed indexed within your speed tier's window, the task automatically moves to `FAILED` and is refunded - you are never charged for a link that was never actually confirmed indexed.

There's no webhook for status changes yet - poll `GET /tasks/:id` or `GET /tasks?status=DONE` to detect completion.

---

## Endpoints

---

### GET /auth/me - Get your profile

Returns your account info, current balance, and pricePerUrl for your current tier - no need to cross-reference /pricing-plans just to know your own rate.

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/auth/me" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/auth/me", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const profile = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/auth/me",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
profile = res.json()
```

**Example response**

```json
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "email": "you@example.com",
  "balance": "48.230000",
  "tier": "GROWTH",
  "pricePerUrl": "0.016000",
  "role": "USER",
  "emailVerifiedAt": "2026-08-01T12:00:00.000Z",
  "createdAt": "2026-07-15T09:00:00.000Z",
  "totpEnabled": false
}
```

---

### GET /tasks/:id - Get a single task

Returns 1 task by id. Returns 404 if it doesn't exist or belongs to a different user.

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/tasks/TASK_ID" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/tasks/TASK_ID", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const task = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/tasks/TASK_ID",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
task = res.json()
```

**Example response**

```json
{
  "id": "clx2b3c4d5e6f7g8h9i0",
  "userId": "clx0a0b0c0d0e0f0g0h0",
  "url": "https://example.com/page",
  "domain": "example.com",
  "status": "DONE",
  "speedTier": "fast",
  "dripFeed": false,
  "autoAdded": false,
  "cost": "0.020000",
  "idempotencyKey": "a-unique-key-per-request",
  "providerId": "clz9aa0b1c2d3e4f5g6h",
  "externalTaskId": "ext_98213",
  "errorMessage": null,
  "createdAt": "2026-09-05T10:00:00.000Z",
  "submittedAt": "2026-09-05T10:00:05.000Z",
  "doneAt": "2026-09-05T10:04:12.000Z",
  "updatedAt": "2026-09-05T10:04:12.000Z"
}
```

---

### POST /tasks - Submit a URL for indexing

Queues a URL for indexing through our multi-channel indexing pipeline for maximum coverage. Submission is asynchronous: the response comes back PENDING immediately, and dispatch happens from a priority queue right after - poll GET /tasks/:id to see it move to SUBMITTED. Charges your balance immediately once the task is created - not when Google finishes indexing it (see Task lifecycle below).

**Request body**

| Field | Type | Notes |
| --- | --- | --- |
| `url` | `string` | Must be a valid, publicly reachable URL. |
| `idempotencyKey` | `string` | You generate this. Reusing the same key returns the original task instead of creating a duplicate charge. |
| `speedTier` | `string?` | One of "fast", "instant". Default "fast" if omitted. "instant" charges 5x pricePerUrl and jumps the submission queue. See Speed tiers below. |
| `dripFeed` | `boolean?` | Default false. Free add-on, independent of speedTier (works with either "fast" or "instant") - spreads submission out over a few hours instead of right away (safer for large backlink batches). Adds +7 days to the auto-refund window. See Speed tiers below. |

**Example request (cURL)**

```bash
curl -X POST "https://api.iindexer.com/tasks" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/page", "idempotencyKey": "a-unique-key-per-request", "speedTier": "instant", "dripFeed": false}'
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/tasks", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/page",
    idempotencyKey: "a-unique-key-per-request",
    speedTier: "instant",
    dripFeed: false,
  }),
});
const task = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.post(
    "https://api.iindexer.com/tasks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://example.com/page",
        "idempotencyKey": "a-unique-key-per-request",
        "speedTier": "instant",
        "dripFeed": False,
    },
)
task = res.json()
```

**Example response**

```json
{
  "id": "clx2b3c4d5e6f7g8h9i0",
  "userId": "clx0a0b0c0d0e0f0g0h0",
  "url": "https://example.com/page",
  "domain": "example.com",
  "status": "PENDING",
  "speedTier": "instant",
  "dripFeed": false,
  "autoAdded": false,
  "cost": "0.100000",
  "idempotencyKey": "a-unique-key-per-request",
  "providerId": null,
  "externalTaskId": null,
  "errorMessage": null,
  "createdAt": "2026-09-05T10:00:00.000Z",
  "submittedAt": null,
  "doneAt": null,
  "updatedAt": "2026-09-05T10:00:00.000Z"
}
```

---

### POST /tasks/bulk - Submit multiple URLs at once

Same as POST /tasks, for up to 5000 URLs in 1 request (a lower, account-specific limit may apply - see bulk_submit_limit_exceeded below). URLs are grouped by domain: if a domain in the batch has no homepage URL of its own, one is added automatically (autoAdded: true in its result) and charged normally, same as any other URL. Each item is processed independently - 1 bad URL or 1 that fails to dispatch does not block the rest of the batch. Results come back in the same order as items, 1 result per input item (plus any auto-added homepage results appended after).

**Request body**

| Field | Type | Notes |
| --- | --- | --- |
| `items` | `array` | 1-5000 objects, each { url: string, idempotencyKey: string } - same rules as the single POST /tasks fields. |
| `speedTier` | `string?` | One of "fast", "instant". Default "fast" if omitted. Applies to every item in the batch - see Speed tiers below. |
| `dripFeed` | `boolean?` | Default false. Applies to every item in the batch. See Speed tiers below. |

**Example request (cURL)**

```bash
curl -X POST "https://api.iindexer.com/tasks/bulk" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items": [{"url": "https://example.com/page-1", "idempotencyKey": "batch-1-item-1"}, {"url": "https://example.com/page-2", "idempotencyKey": "batch-1-item-2"}], "speedTier": "fast"}'
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/tasks/bulk", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    items: [
      { url: "https://example.com/page-1", idempotencyKey: "batch-1-item-1" },
      { url: "https://example.com/page-2", idempotencyKey: "batch-1-item-2" },
    ],
    speedTier: "fast",
  }),
});
const results = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.post(
    "https://api.iindexer.com/tasks/bulk",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "items": [
            {"url": "https://example.com/page-1", "idempotencyKey": "batch-1-item-1"},
            {"url": "https://example.com/page-2", "idempotencyKey": "batch-1-item-2"},
        ],
        "speedTier": "fast",
    },
)
results = res.json()
```

**Example response**

```json
[
  {
    "url": "https://example.com/page-1",
    "autoAdded": false,
    "task": {
      "id": "clx2b3c4d5e6f7g8h9i0",
      "userId": "clx0a0b0c0d0e0f0g0h0",
      "url": "https://example.com/page-1",
      "domain": "example.com",
      "status": "PENDING",
      "speedTier": "fast",
      "dripFeed": false,
      "autoAdded": false,
      "cost": "0.020000",
      "idempotencyKey": "batch-1-item-1",
      "providerId": null,
      "externalTaskId": null,
      "errorMessage": null,
      "createdAt": "2026-09-05T10:00:00.000Z",
      "submittedAt": null,
      "doneAt": null,
      "updatedAt": "2026-09-05T10:00:00.000Z"
    }
  },
  {
    "url": "https://example.com/page-2",
    "autoAdded": false,
    "task": { "...": "same shape as above" }
  },
  {
    "url": "https://example.com/",
    "autoAdded": true,
    "task": { "...": "homepage task, added automatically because the batch had no homepage URL for example.com" }
  }
]
```

---

### GET /tasks - List your tasks

Returns your submitted tasks, newest first. Filter by status and paginate.

**Query parameters**

| Field | Type | Notes |
| --- | --- | --- |
| `status` | `string?` | One of PENDING, SUBMITTED, PROCESSING, DONE, FAILED. |
| `url` | `string?` | Substring filter on the URL, case-insensitive. |
| `domain` | `string?` | Exact-match filter - e.g. pass the domain from GET /tasks/grouped-by-domain to list only its tasks. |
| `dripFeed` | `boolean?` | Filter to only drip-fed (true) or only non-drip-fed (false) tasks. |
| `page` | `number?` | Default 1. |
| `pageSize` | `number?` | Default 20, max 100. |

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/tasks?status=DONE&page=1&pageSize=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const params = new URLSearchParams({ status: "DONE", page: "1", pageSize: "20" });
const res = await fetch(`https://api.iindexer.com/tasks?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { items, total } = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/tasks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"status": "DONE", "page": 1, "pageSize": 20},
)
data = res.json()
```

**Example response**

```json
{
  "items": [
    {
      "id": "clx2b3c4d5e6f7g8h9i0",
      "userId": "clx0a0b0c0d0e0f0g0h0",
      "url": "https://example.com/page",
      "domain": "example.com",
      "status": "DONE",
      "speedTier": "fast",
      "dripFeed": false,
      "autoAdded": false,
      "cost": "0.020000",
      "idempotencyKey": "a-unique-key-per-request",
      "providerId": "clz9aa0b1c2d3e4f5g6h",
      "externalTaskId": "ext_98213",
      "errorMessage": null,
      "createdAt": "2026-09-05T10:00:00.000Z",
      "submittedAt": "2026-09-05T10:00:05.000Z",
      "doneAt": "2026-09-05T10:04:12.000Z",
      "updatedAt": "2026-09-05T10:04:12.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
```

---

### GET /tasks/grouped-by-domain - List your tasks grouped by domain

Rolls up your tasks by domain (the "www." prefix is stripped so www.example.com and example.com count as the same site; subdomains like blog.example.com are kept separate). Display-only - does not affect billing or task status. Use the domain field from a result as the domain filter on GET /tasks to list that group's tasks.

**Query parameters**

| Field | Type | Notes |
| --- | --- | --- |
| `status` | `string?` | One of PENDING, SUBMITTED, PROCESSING, DONE, FAILED. |
| `domain` | `string?` | Substring filter on the domain name, case-insensitive. |
| `dripFeed` | `boolean?` | Filter to only drip-fed (true) or only non-drip-fed (false) tasks. |
| `page` | `number?` | Default 1. |
| `pageSize` | `number?` | Default 20, max 100. |

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/tasks/grouped-by-domain?page=1&pageSize=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/tasks/grouped-by-domain?page=1&pageSize=20", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { items, total } = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/tasks/grouped-by-domain",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"page": 1, "pageSize": 20},
)
data = res.json()
```

**Example response**

```json
{
  "items": [
    {
      "domain": "example.com",
      "count": 12,
      "totalCost": "0.24",
      "statusCounts": { "DONE": 10, "FAILED": 2 },
      "latestCreatedAt": "2026-09-06T10:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
```

---

### GET /transactions - List your transactions

Returns your balance history - deposits, task charges, refunds, and admin adjustments - newest first.

**Query parameters**

| Field | Type | Notes |
| --- | --- | --- |
| `page` | `number?` | Default 1. |
| `pageSize` | `number?` | Default 20, max 100. |

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/transactions?page=1&pageSize=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const params = new URLSearchParams({ page: "1", pageSize: "20" });
const res = await fetch(`https://api.iindexer.com/transactions?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { items, total } = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/transactions",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"page": 1, "pageSize": 20},
)
data = res.json()
```

**Example response**

```json
{
  "items": [
    {
      "id": "clx3c4d5e6f7g8h9i0j1",
      "userId": "clx0a0b0c0d0e0f0g0h0",
      "type": "TASK_CHARGE",
      "status": "COMPLETED",
      "amount": "-0.020000",
      "balanceAfter": "48.210000",
      "idempotencyKey": "a-unique-key-per-request:charge",
      "relatedTaskId": "clx2b3c4d5e6f7g8h9i0",
      "relatedPaymentIntentId": null,
      "createdAt": "2026-09-05T10:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
```

---

### GET /transactions/:id - Get a single transaction

Returns 1 transaction by id. Returns 404 if it doesn't exist or belongs to a different user. Note: "lifetime-usage" is a reserved path segment matched by the endpoint below before it ever reaches this route, so it can never collide with a real transaction id.

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/transactions/TRANSACTION_ID" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/transactions/TRANSACTION_ID", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const transaction = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/transactions/TRANSACTION_ID",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
transaction = res.json()
```

**Example response**

```json
{
  "id": "clx3c4d5e6f7g8h9i0j1",
  "userId": "clx0a0b0c0d0e0f0g0h0",
  "type": "TASK_CHARGE",
  "status": "COMPLETED",
  "amount": "-0.020000",
  "balanceAfter": "48.210000",
  "idempotencyKey": "a-unique-key-per-request:charge",
  "relatedTaskId": "clx2b3c4d5e6f7g8h9i0",
  "relatedPaymentIntentId": null,
  "createdAt": "2026-09-05T10:00:00.000Z"
}
```

---

### GET /transactions/lifetime-usage - Get lifetime credits used

Returns the total balance ever spent on tasks, net of refunds - a single running total, not paginated.

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/transactions/lifetime-usage" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/transactions/lifetime-usage", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { lifetimeCreditsUsed } = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get(
    "https://api.iindexer.com/transactions/lifetime-usage",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
lifetime_credits_used = res.json()["lifetimeCreditsUsed"]
```

**Example response**

```json
{
  "lifetimeCreditsUsed": "182.440000"
}
```

---

### GET /pricing-plans - List pricing plans

Public reference for all plan tiers. There's no recurring monthly charge: pricePerUrl is what actually gets debited from your balance per task (see /tasks), and priceUsd is the one-time price to purchase that tier from the dashboard (upgrades your tier and tops up your balance - this isn't callable with an API key, only from the iIndexer dashboard UI). quotaPerMonth is a reference figure only and isn't enforced as a hard limit.

_No authentication required._

**Example request (cURL)**

```bash
curl "https://api.iindexer.com/pricing-plans"
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/pricing-plans");
const plans = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.get("https://api.iindexer.com/pricing-plans")
plans = res.json()
```

**Example response**

```json
[
  {
    "tier": "STARTER",
    "name": "Starter",
    "pricePerUrl": "0.020000",
    "quotaPerMonth": 500,
    "priceUsd": "10.000000"
  }
]
```

---

### POST /url-checks/availability - Check URL availability (free)

Checks whether a URL is even worth indexing, before you pay for /tasks or /url-checks/index-status: HTTP status (flags 404/410/451), a noindex meta tag or X-Robots-Tag header, robots.txt disallow rules (checked against Googlebot), and non-indexable Content-Type (images/video/audio/archives). Free - does not touch your balance. Requests are made from our servers with SSRF protections (private/reserved IP ranges are blocked, redirects are re-validated, requests time out after 8s).

**Request body**

| Field | Type | Notes |
| --- | --- | --- |
| `url` | `string` | The URL to check. |

**Example request (cURL)**

```bash
curl -X POST "https://api.iindexer.com/url-checks/availability" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/page"}'
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/url-checks/availability", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com/page" }),
});
const result = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.post(
    "https://api.iindexer.com/url-checks/availability",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"url": "https://example.com/page"},
)
result = res.json()
```

**Example response**

```json
{
  "url": "https://example.com/page",
  "httpStatus": 200,
  "unavailable": false,
  "noindex": false,
  "robotsDisallowed": false,
  "nonIndexableContentType": false,
  "contentType": "text/html; charset=utf-8",
  "fetchError": null
}
```

---

### POST /url-checks/index-status - Check Google index status

Checks whether a URL is already indexed by Google (via an independent search check - the same mechanism used to auto-complete your submitted tasks). Costs $0.005/URL (5 credits), charged from your balance immediately - this charge is NOT refunded, regardless of the outcome (already indexed, not indexed, or the check itself failing) - the underlying search check has a real cost to run either way. Requires the same client-generated idempotencyKey pattern as /tasks - retrying with the same key returns the original result, without charging twice.

**Request body**

| Field | Type | Notes |
| --- | --- | --- |
| `url` | `string` | The URL to check. |
| `idempotencyKey` | `string` | You generate this. Reusing the same key returns the original result instead of creating a duplicate charge. |

**Example request (cURL)**

```bash
curl -X POST "https://api.iindexer.com/url-checks/index-status" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/page", "idempotencyKey": "a-unique-key-per-request"}'
```

**Example request (JavaScript)**

```javascript
const res = await fetch("https://api.iindexer.com/url-checks/index-status", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com/page", idempotencyKey: "a-unique-key-per-request" }),
});
const result = await res.json();
```

**Example request (Python)**

```python
import requests

res = requests.post(
    "https://api.iindexer.com/url-checks/index-status",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"url": "https://example.com/page", "idempotencyKey": "a-unique-key-per-request"},
)
result = res.json()
```

**Example response**

```json
{
  "url": "https://example.com/page",
  "indexed": true,
  "refunded": false,
  "cost": "0.005"
}
```
