A backup collects everything behind one link in the background. Send a profile, board or pin URL with a credit budget, and Upscrape finds every board, collects each one, saves the results as they arrive and picks up where it left off after an interruption. You read progress, page through saved records or download them as CSV or JSON Lines.

Backups are available for Pinterest today, through the API.

## Which links work

| Link | What is collected |
| --- | --- |
| Profile, for example `https://www.pinterest.com/pinterest/` or `.../pinterest/_saved/` | Every public board on the profile, each collected on its own |
| Board, for example `https://www.pinterest.com/pinterest/home-decor-ideas/` | Every pin on the board, with the sections each pin belongs to |
| Pin, for example `https://www.pinterest.com/pin/549157816661240123/` | That one pin |

Country subdomains such as `uk.pinterest.com` work. Use the full `pinterest.com` link rather than a `pin.it` short link. A link that matches none of these returns HTTP `422`.

## Start a backup

```http
POST https://data.upscrape.com/v1/backups
Authorization: Bearer UPSCRAPE_API_KEY
Content-Type: application/json
Idempotency-Key: UNIQUE_LOGICAL_OPERATION
```

```json
{
  "url": "https://www.pinterest.com/pinterest/",
  "max_credits": 500
}
```

| Field | Required | Meaning |
| --- | --- | --- |
| `url` | Yes | The profile, board or pin link |
| `max_credits` | Yes | The most this backup may spend in total, a whole number from 1 to 100,000 |

The `Idempotency-Key` header is required. Sending the same key and body again returns the same backup instead of starting a second one; the same key with a different body returns HTTP `409`.

The response is HTTP `202` with the backup's `backup_id`, its `state` and links to follow it: `poll_url`, `targets_url` and `records_url`.

## Follow progress

Poll `GET /v1/backups/{id}`. It returns HTTP `202` while the backup is running and HTTP `200` once it is paused or finished. An abbreviated response:

```json
{
  "backup_id": "5b0c8a52-6d0e-4f7e-9a51-0f3c2d6b1a77",
  "state": "running",
  "reason": null,
  "progress": {
    "targets": 238,
    "states": {"complete": 120, "running": 2, "pending": 116},
    "saved_memberships": 9814,
    "discovery_finished": true
  },
  "max_credits": 500,
  "credits_charged": 131.0,
  "expires_at": "2026-10-31T18:40:00Z"
}
```

`state` is `running`, `paused` or `finished`. A target is one board, or the single pin. Each target is `pending`, `running`, `paused`, `complete` or `incomplete`, with a `reason` when it did not complete. `saved_memberships` counts saved pins per board, so a pin saved on two boards counts twice.

**Finished means the backup has done everything it can, not that every board is complete.** Pinterest sometimes reports more pins on a board than it actually shows; those boards end as `incomplete` with a reason. Page through every target with `GET /v1/backups/{id}/targets?after=0`, then pass `next_after` back until it is `null`. Each page holds up to 100 targets.

For a profile, boards are found one page at a time while earlier boards are already being collected, and up to two boards are collected at once per account.

## Read the results

Page through saved records while the backup runs or after it ends:

```http
GET https://data.upscrape.com/v1/backups/{id}/records
```

Each page holds up to 250 records. Pass `next_cursor` back unchanged as `?cursor=` until it is `null`. Each record has the pin `id`, `board_id`, `board_url`, `section_ids` and the full pin as `data`, in the same shape the platform's capabilities return.

To download everything in one file once work has settled:

```http
GET https://data.upscrape.com/v1/backups/{id}/export?format=csv
```

`format` is `jsonl` (the default, one record per line) or `csv`. CSV columns are `id`, `board_id`, `board_url`, `section_ids` (a JSON array) and `data` (a JSON object). Every cell is quoted, and text that a spreadsheet could read as a formula is prefixed with `'`. Media stays as links to the original files.

The export is ready when the backup is finished, or paused with nothing still running. While work is still being saved it returns HTTP `409`; wait and try again.

## Pause, resume and budget

- `POST /v1/backups/{id}/pause` stops new work. Requests already in progress may still finish and are charged; everything saved stays.
- `POST /v1/backups/{id}/resume` continues from saved progress. Send `{"max_credits": 800}` to raise the total budget; it can stay the same or go up, never down.

When the budget runs out, the backup pauses with reason `credit_limit_reached` and keeps everything it saved. Raise `max_credits` on resume to continue.

Each request a backup makes is charged at that capability's published price, the same as calling it yourself, and the total never goes above `max_credits`. `credits_charged` shows the running total. See the [platform's prices](/docs/reference/platforms) for what each request costs.

## Limits

- Up to 10,000 boards per backup. Profile discovery pauses with a reason when it reaches that.
- Up to 50,000 pins and 100 MiB of results per board.
- Up to 100 retained backups per account, and 512 MiB of collected results across the account.
- A backup and its results are kept until `expires_at`, 30 days after it started. One that is still running or paused on that date is kept until it finishes and is removed soon after, so download it once it finishes.

## Errors

Backup requests report problems with the HTTP status and a plain `error.message`:

| HTTP | When |
| --- | --- |
| `403` | The API key cannot use this capability or this backup. Starting, pausing and resuming need the `execute` scope; reading needs `jobs:read` |
| `404` | No backup with that ID on this account |
| `409` | The idempotency key belongs to a different backup, the backup cannot be paused, or the export is not ready yet |
| `422` | Unsupported link, missing or invalid `max_credits` or `Idempotency-Key`, resuming a backup that is not paused or with a lower budget, or a changed cursor |
| `429` | The account has reached its retained backup limit |

## Media files

Exports keep each pin's image and video links. Copying the files themselves into downloadable ZIP parts is not available yet; when it is, a backup will accept `include_media` and list its parts at `GET /v1/backups/{id}/media`. Until then, sending `include_media: true` returns HTTP `422`.
