> ## Documentation index
> Fetch the complete documentation index at: https://docs.leadity.io/llms.txt
> Use this file to discover all available pages before exploring further.

# List scrape offers

> List what a finished scrape can still sell, with prices. Free.

Answers, for free, every upgrade a finished scrape can still sell and what each costs in credits: its held-back businesses by cell, the phone line types of its leads, email addresses and their verification, and owner names. Each offer is null when there is nothing to sell, it was already bought, or the scrape is still running. Read it before any of the five purchase operations: the counts are recomputed on every call, so a business bought elsewhere since falls out before it can be sold twice.

`GET https://api.leadity.io/v1/scrapes/{id}/offers`

- Scope: `read`
- Spends credits: no
- MCP tool: `list_scrape_offers`

## Headers

- `Authorization`, string, required. Bearer, a space, then an API key: Bearer lk_api_... An MCP key is refused here with wrong_key_kind.

## Path parameters

- `id`, string, required. The scrape's id, as create_scrape and list_scrapes answer it. 1 to 64 characters, matches ^[A-Za-z0-9]{1,64}$.

## Example request

```bash cURL
curl "https://api.leadity.io/v1/scrapes/Vb7nW3xQ9k/offers" \
  -H "Authorization: Bearer $LEADITY_API_KEY"
```

```javascript Node.js
const response = await fetch("https://api.leadity.io/v1/scrapes/Vb7nW3xQ9k/offers", {
  headers: {
    Authorization: `Bearer ${process.env.LEADITY_API_KEY}`,
  },
});

const data = await response.json();
console.log(data);
```

```python Python
import os

import requests

response = requests.get(
    "https://api.leadity.io/v1/scrapes/Vb7nW3xQ9k/offers",
    headers={
        "Authorization": f"Bearer {os.environ['LEADITY_API_KEY']}",
    },
)

print(response.json())
```

## Response

- `residuals`, object | null, required. The businesses the scrape found and did not deliver, bought by purchase_residuals. Null when there are none to sell.
  - `phoneAxis`, string, required. typed when the order bought a line type and its cells split by type; untyped when its numbers are one with_phone bucket. One of: `typed`, `untyped`.
  - `cells`, object[], required. Every cell holding at least one business. A cell not listed holds none.
    - `phoneType`, string, required. The phone bucket: a line type, with_phone, no_phone or any. One of: `any`, `mobile`, `landline`, `voip`, `toll_free`, `with_phone`, `no_phone`.
    - `website`, string, required. Whether the business has a website: has, none or any. One of: `any`, `has`, `none`.
    - `email`, string, required. The email bucket: none, unverified, verified or any. One of: `any`, `none`, `unverified`, `verified`.
    - `niche`, string, required. The Google category class: match (its main category is an ordered niche), other, unknown, any, or category for one named category beside it. One of: `any`, `match`, `other`, `unknown`, `category`.
    - `category`, string, optional. The key of one named category from the offer's categories. Present exactly when niche is category. 1 to 200 characters.
    - `count`, integer, required. Businesses the cell holds.
    - `credits`, integer, required. What buying the whole cell costs.
  - `categories`, object[], required. Up to ten named categories inside other, most businesses first, each buyable as niche category.
    - `key`, string, required.
    - `label`, string, required.
  - `suggested`, object | null, required. The cell most like what was ordered, when there is one.
    - `phoneType`, string, required. The phone bucket: a line type, with_phone, no_phone or any. One of: `any`, `mobile`, `landline`, `voip`, `toll_free`, `with_phone`, `no_phone`.
    - `website`, string, required. Whether the business has a website: has, none or any. One of: `any`, `has`, `none`.
    - `email`, string, required. The email bucket: none, unverified, verified or any. One of: `any`, `none`, `unverified`, `verified`.
    - `niche`, string, required. The Google category class: match (its main category is an ordered niche), other, unknown, any, or category for one named category beside it. One of: `any`, `match`, `other`, `unknown`, `category`.
    - `category`, string, optional. The key of one named category from the offer's categories. Present exactly when niche is category. 1 to 200 characters.
- `phoneReveal`, object | null, required. The phone line types of the delivered leads, bought by purchase_phone_reveal. Only on a scrape that ordered no phone filter.
  - `count`, integer, required. Delivered numbers the reveal covers.
  - `credits`, integer, required. What the reveal costs.
- `emailFind`, object | null, required. Email addresses for the delivered leads, bought by purchase_email_find.
  - `pool`, integer, required. Delivered leads with a website nobody has read.
  - `minTarget`, integer, required. The smallest target purchase_email_find takes.
  - `creditsPerResult`, number, required. Credits per address found.
- `emailVerify`, object | null, required. Verdicts for the addresses held, bought by purchase_email_verify.
  - `pool`, integer, required. Addresses held that nobody has verified.
  - `minTarget`, integer, required. The smallest target purchase_email_verify takes.
  - `creditsPerResult`, number, required. Credits per billable verdict.
- `ownerNames`, object | null, required. Owner names for the delivered leads, bought by purchase_owner_names.
  - `pool`, integer, required. Delivered leads with a phone and no owner name that no run has tried.
  - `minTargetNames`, integer, required. The smallest targetNames purchase_owner_names takes.
  - `creditsPerName`, number, required. Credits per confirmed name.

## Example response

```json 200
{
  "residuals": {
    "phoneAxis": "typed",
    "cells": [
      {
        "phoneType": "landline",
        "website": "has",
        "email": "any",
        "niche": "match",
        "count": 642,
        "credits": 449
      }
    ],
    "categories": [
      {
        "key": "gutter cleaning service",
        "label": "Gutter cleaning service"
      }
    ],
    "suggested": {
      "phoneType": "landline",
      "website": "any",
      "email": "any",
      "niche": "match"
    }
  },
  "phoneReveal": null,
  "emailFind": {
    "pool": 718,
    "minTarget": 10,
    "creditsPerResult": 0.4
  },
  "emailVerify": null,
  "ownerNames": null
}
```

## Errors

Every refusal answers `{ error, message }` with the status below. The full list is on the [Errors](https://docs.leadity.io/errors) page.

| Code | Status | Meaning | Extra fields |
| --- | --- | --- | --- |
| `unauthorized` | 401 | The API key is missing, malformed, revoked or expired. | - |
| `wrong_key_kind` | 401 | This key opens the other surface: an API key works on the HTTP API, an MCP key on the MCP server. | - |
| `account_suspended` | 403 | The account is suspended. | - |
| `email_not_verified` | 403 | The account's email address is not verified yet. | - |
| `invalid_body` | 400 | The request does not match the operation's schema. | `issues`: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there). |
| `not_found` | 404 | No such resource on this account. | `id`: On export_scrapes: the first id that is not one of the account's scrapes. |
| `rate_limited` | 429 | Too many requests. Wait for the Retry-After seconds and try again. | `retryAfterSeconds`: The seconds to wait, the same number as the Retry-After header. |
| `try_again` | 503 | Leadity could not answer just now. Retry in a moment, with the same Idempotency-Key on a spending call. | `retryAfterSeconds`: When a request with this Idempotency-Key is still being processed: the seconds to wait before retrying with the same key, as the Retry-After header says. |
| `internal_error` | 500 | Something failed on our side. Nothing was charged twice. | `body`: On the MCP server, when Leadity answered in a shape the tool does not publish: that answer as it came. |
