---
title: Bring your own provider key
description: Connect your own API key for a provider so runs on it use your account, not looot's, and know what that changes about the price.
---

looot can run a job on a provider using looot's own platform key, or on a key you connect
yourself. Connecting your own key does not change how you call [`run`](/mcp-tools/run): you still send
`job:<id>` or an endpoint id, and looot picks the connected key over the platform one.

## Connect a key

In the looot dashboard, open **Keys** in the sidebar (`looot.ai/connections`). Pick the provider,
paste the key, and save. The dashboard shows:

> Saved. Runs on \{provider\} now use your key instead of looot's. Search and inspect show what
> each run costs you.

The key is connected at once. No test call runs against the provider when you save it, so a bad key
is only caught the first time a run uses it.

To do the same from the API, register a credential with
`connections:write` scope:

```bash
curl -s -X POST "https://api.looot.ai/v1/connections" \
  -H "Authorization: Bearer $LOOOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-hunter-key",
    "providerId": "hunter",
    "label": "My Hunter key",
    "secret": "<your provider api key>"
  }'
```

`kind` defaults to `api_key` when left out. Other kinds exist for a provider that needs OAuth
(`oauth`), a service account, a credential file, a signed request, or a CLI-issued token
(`cli_auth`); `POST /v1/connections/oauth/start` begins the OAuth flow for a provider that uses it.

The connection's `status` moves through `draft`, `authorization_pending`, `connected`, `degraded`,
`expired`, `action_required` and `revoked`. `POST /v1/connections/{connectionId}/test` and `GET
/v1/connections/{connectionId}/test-receipts` let you check it before relying on it in production.
`POST /v1/connections/{connectionId}/rotate` swaps in a new secret without losing the connection's
id; `POST /v1/connections/{connectionId}/revoke` disconnects it.

## What a run on your own key costs

looot charges $0 for a run on your own key. The exception is an endpoint with a commercial price
when your workspace has a price policy on it. When that combination applies, the run still charges the
listed price. `estimatedPrice` and `customerPrice` on [`search`](/mcp-tools/search) and [`inspect`](/mcp-tools/inspect) rows show 0 for your
own connected key when no price policy applies. Check those two fields before you run.
Either way, your provider account is billed directly for the call: connecting your key does not
make the provider itself free, only looot's fee.

```json MCP
search {"filters": {"capability": "people.email.find"}}
```

Read `credential` on the row: `yours` means this workspace's connected key would run it,
`platform` means looot's key would, `platform_own_key_optional` means either works and looot's key
runs it by default, `own_key_required` means only your own key can run this endpoint,
`unavailable` means neither can right now, and `none` means the endpoint needs no key.

## Run it

Once connected, `run` on that provider's job or endpoint works exactly as it does on looot's own
key. looot's credential ladder picks your connected key ahead of the platform one automatically:

```json MCP
run {"endpointId": "job:people.email.find", "input": {"first_name": "Jane", "last_name": "Doe", "domain": "example.com"}, "idempotencyKey": "byok-jane-doe-1", "fallback": true}
```

`inspect` on the endpoint shows `endpoint.credential` for your workspace, and `estimatedMaxCost`
reflects your own key's price when it is 0.

## On a miss or error

- The key is wrong or revoked at the provider: the run fails and, on your own key, stops rather
  than falling back to looot's key for that endpoint (a rejected call on your own credential does
  not retry on a different one inside the same route).
- A connection stuck in `action_required`: a secondary credential the provider needs is still
  missing; `pendingCredentialRoles` on the connection names it. Add it with `POST
  /v1/connections/{connectionId}/credentials/{role}`.
- `expired` or `degraded`: rotate the key with `POST /v1/connections/{connectionId}/rotate`, or
  reconnect it from **Keys** in the dashboard.

<Related />
