<!-- source: https://secondlayer.tools/docs/authentication -->

# Keys and billing

One account key opens everything hosted: Index, Streams, your subgraphs and webhooks, and the archive. Prepaid credits pay for it.

## Your key

### Create it

[Sign in](https://www.secondlayer.tools/signin) with your email. Your first key is shown once on [your account page](https://www.secondlayer.tools/account), where you can create more. Set it as `SECONDLAYER_API_KEY` ([Introduction](/docs#get-started)); the SDK, CLI and MCP pick it up on the hosted URL.

### Set it

The SDK, CLI and MCP read both variables on the hosted URL.

```bash

```

### Read with it

A live page from mainnet:

```bash
curl -H "Authorization: Bearer $SECONDLAYER_API_KEY" \
  "$SECONDLAYER_API_URL/v1/index/events?event_type=ft_transfer&limit=1"
```

```json response.json status=200
{
  "events": [
    {
      "cursor": "9194709:1",
      "block_height": 9194709,
      "event_type": "ft_transfer",
      "contract_id": "SP1202DRPN7M3EBS9G0ZFZG8C1DD5Q7Q2RAF01TWR.now-stxcity",
      "sender": "SP1202DRPN7M3EBS9G0ZFZG8C1DD5Q7Q2RAF01TWR",
      "recipient": "SP223ARVEMS15JFW0QJ694E64CY58M6HAW3Y9YN4J",
      "amount": "20000000000000"
    }
  ],
  "next_cursor": "9194709:1",
  "tip": { "block_height": 9211973, "finalized_height": 9211575, "lag_seconds": 19 },
  "reorgs": []
}
```

Signed in, **Fill in a docs key** on any sample puts a docs key wherever it reads `SECONDLAYER_API_KEY`. It reads Streams and Index only, expires after 24 hours, and shows on your account page, where you can revoke it. Copy gives the real key; the page shows only its last four characters.

- Your own box takes a different credential, the instance token: [Self-host](/docs/self-host#instance-token).
- Never put `sk-sl_*` in `INSTANCE_TOKEN`, or the hex token in `SECONDLAYER_API_KEY`.

## What it costs

Reads, live or history, priced the same. Block headers and reads of your own subgraph tables are free.

| Rows delivered | Cost |
| --- | --- |
| First 1M rows per account per month | free |
| Past 1M rows/month | $5 per 1M rows, $2 per 1M past $50 in a month |
| `304` on `If-None-Match` for a finalized page | free |

| Example month | |
| --- | --- |
| 3.2M rows read | 1M free + 2.2M × $5/M = $11.00 |
| Hosted stack running webhooks only, at its minimum | $5.00 |
| **Total** | **$16.00** |

### How rows are counted

Block headers (`/v1/index/blocks`) are free, never billed and never a `402`; you pay for event and transaction rows. Reads of your own subgraph tables are free. A hosted subgraph's indexing bills the event and transaction rows it reads.

The SDK sends `If-None-Match` for you. Its cache holds the last 64 pages in memory; pass `etagCache` to keep one across runs.

Past the free monthly allowance, a key with no balance gets a `402` (`insufficient_credits`) with a top-up link, before anything is served. A key still under the allowance, or with a balance, reads normally and is billed per row after the response.

Your hosted stack runs your webhooks and subgraphs, so a webhook-only account has one too. It bills memory plus storage while it runs, and a stopped stack bills nothing.

| Hosted stack | Minimum while it runs |
| --- | --- |
| Webhooks only | $5/mo |
| With subgraphs | $10/mo |

- With no active webhooks and no subgraphs, the stack stops on its own within minutes.
- It never stops in its first 15 minutes, so you have time to create your first webhook.
- Your next request through the API starts it again, and your tables are kept.

## Spend cap

Set a monthly cap on [your credits page](https://www.secondlayer.tools/account/credits#cap). It counts what you spend in credits this month.

| At | What happens |
| --- | --- |
| 80% of the cap | an email, once a month |
| the cap | reads past your free 1M rows answer `402 spend_cap_reached`, archive fetches stop, and your hosted stack stops within minutes, so webhooks stop delivering and subgraphs stop indexing, until the next month |

Raise the cap on the same page and reads resume; the stack starts again within 5 minutes. Remove it and there is no cap; the prepaid balance is the only limit. A hard cap is the tradeoff: past it, your server stops hearing about the chain.

## Balance alerts

At a zero balance your hosted stack stops within minutes: webhooks stop delivering (events are held) and subgraphs stop indexing until you add credits; your tables are kept and it starts again on top-up. Two switches on [your credits page](https://www.secondlayer.tools/account/credits) email you first.

| Switch | Emails when |
| --- | --- |
| 7 days | runway drops under 7 days at your current spend |
| 2 days | runway drops under 2 days, and again if the stack stops |

A top-up that puts runway back over 7 days re-arms both.

## Archive fetches

Bootstrap, backfill, and reindex from the [signed public archive](/docs/archive) draw from your credits per partition bundle fetched. Verification is always free.

- `bootstrap` and `repair` quote the price against your balance before charging.
- Partitions arrive over short-lived presigned URLs, digest-checked before any row is written.
- Your first six repair bundles each month are free.
- Packs are $10, $25, $50, or $100.

| Partition | Price |
| --- | --- |
| `blocks`, `transactions` | $0.05 |
| `events`, `state_writes` | $0.15 |

- Same balance as rows and your stack; it's the hard cap.

```bash
secondlayer credits buy --email you@example.com --pack 25
secondlayer credits balance
```

- `credits buy` opens a one-time card checkout against [archive.secondlayer.tools](https://archive.secondlayer.tools).
- `credits balance` and metered bootstrap use your `secondlayer login` session if you have one, else `SECONDLAYER_API_KEY`.

Auto-refill is off until you set it: `secondlayer credits refill --below 5 --pack 25`.

## Webhook signing secrets

A **signing secret** (`whsec_…`) is per-webhook. It verifies an incoming webhook came from your instance; see [Webhooks](/docs/webhooks).

- Check it with `verifyWebhookSignature`.
- Never in `Authorization`.
- Rotate with `secondlayer webhooks rotate-secret`.

## Notes for agents

- Hosted: export SECONDLAYER_API_URL=https://api.secondlayer.tools and SECONDLAYER_API_KEY ('sk-sl_*', from /account/keys). Send it as 'Authorization: Bearer $SECONDLAYER_API_KEY'. The SDK, MCP and CLI pick the key from the host; set the same two variables in CI.
- Never put the account key in INSTANCE_TOKEN.
- 1M rows a month are free; set a monthly spend cap and both balance alerts at /account/credits.
- '402 spend_cap_reached' means the monthly cap is hit; '402 insufficient_credits' means the balance is empty. Raise the cap or buy credits to clear each.
- The cap covers every charge: at the cap (and at a zero balance) the hosted stack stops, so webhooks stop delivering and subgraphs stop indexing until it is cleared.
- A hosted stack with no active webhooks and no subgraphs stops itself (never in its first 15 minutes) and restarts on the next request. Pausing every webhook stops its billing.
- Rotate a webhook signing secret with 'secondlayer webhooks rotate-secret', update the receiver that calls verifyWebhookSignature, then confirm nothing still references the old value.
- Read sample: 'GET /v1/index/events?event_type=ft_transfer&limit=1'; event_type is required on /v1/index/events.

## Setup

Ensure setup once, skipping any step already done:
- Skill: `bunx skills add ryanwaits/secondlayer --skill secondlayer -y`
- CLI: `bun add -g @secondlayer/cli`
- Instance: `secondlayer init` writes `INSTANCE_TOKEN`. Loopback `/v1` reads need no key.
- Hosted/archive: export `SECONDLAYER_API_KEY` (`sk-sl_*`) or buy credits with `secondlayer credits buy` (`balance`, `refill`), then `secondlayer whoami`
