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

# Billing

> Pay as you go, billed per 1,000 results: how a job's cost is worked out, capped and reported.

Pay as you go, billed per 1,000 results. You buy credits up front, and each job spends them per result it delivers.
Each scraper has its own rate, and every job reports its final cost.

## Rates

The current rates are on the [pricing page](https://go-fetch.io/pricing/), and in the price list, which needs no key:

```bash cURL theme={null}
curl https://go-fetch.io/api/v1/pricing/
```

```python Python theme={null}
import json
import urllib.request

with urllib.request.urlopen("https://go-fetch.io/api/v1/pricing/") as response:
    for row in json.load(response)["results"]:
        print(row["platform"], row["price_per_1000"], row["price_per_item"])
```

The price list includes only public scrapers. Each row has `platform` (the scraper type), `price_per_1000` and
`price_per_item`, in USD as decimal strings, plus `minimum_charge_enabled` and `minimum_charge_usd`
([minimum charge](#what-a-job-costs)).
`instagram_posts` and `instagram_profile` bill at the `instagram` rate; `facebook_posts` and `facebook_profile` at the
`facebook` rate.

A crawl limited to posts from the last 29 days is billed at its own rate. The dashboard shows that rate before you
start the job, and every job keeps the rate it was billed at. The price list shows the rate for a full crawl, and
`POST /api/v1/pricing/calculate_cost/` estimates at that rate:

```bash theme={null}
curl https://go-fetch.io/api/v1/pricing/calculate_cost/ \
  -H "Content-Type: application/json" \
  -d '{"platform": "instagram", "items_count": 5000}'
```

A job's own rate is in `price_per_1000_at_creation`, and `is_initial_crawl` says whether it was billed as a full crawl
(`true`) or a recent one (`false`).

## What a job costs

A job costs its results times its scraper's rate, locked when the job is created. A result is one row of output: a
post, comment, video, profile, page or search query, depending on the scraper. A new rate applies to new jobs only.

* **You pay per billed result row delivered. A job that delivers zero billed rows costs \$0.**
* Some scrapers also return rows that are not posts or videos. Instagram and Facebook start each profile with a
  summary row, which is billed. Status rows on YouTube and Instagram cost nothing and stay in the results: a channel
  with nothing in your date range, a link that is not a channel, a private, missing, restricted or blocked profile,
  a profile that couldn't be resolved (`profile_error`), a post that couldn't be read. Each
  [scraper guide](https://docs.go-fetch.io/scrapers/instagram.md) lists its rows and whether they are billed. `/results/` `total` includes these
  \$0 rows and can exceed `items_scraped`, which counts billed rows.
* Some scrapers charge a minimum per job. In the price list, `minimum_charge_enabled` says whether a scraper has one
  and `minimum_charge_usd` gives the amount, or `null` when there is none. A job's `minimum_charge_at_creation` says
  whether its scraper had one when it was created. A job with no billed results costs \$0 even then.
* The charge is settled when a job completes, or, for a job its own [spending cap](#cap-a-job-with-maxtotalchargeusd)
  stops, when the stopped run reports the rows it delivered. A job you cancel yourself is not charged.
* No charge is ever above the job's `maxTotalChargeUsd`.

Two rows of the price list, cut to the minimum fields:

```json theme={null}
[
  {"platform": "instagram", "minimum_charge_enabled": true, "minimum_charge_usd": "0.01"},
  {"platform": "tiktok_search", "minimum_charge_enabled": false, "minimum_charge_usd": null}
]
```

## The final cost

Every job reports what it was charged, in three places that always agree:

* `actual_cost` on the job (`GET /api/v1/jobs/{id}/`), in USD;
* `usageTotalUsd` in its [webhook](https://docs.go-fetch.io/webhooks.md), and in the SDK's run;
* the job's line in your billing history and in `GET /api/v1/usage/`.

`estimated_cost` on the job is the estimate its credit check used.

## Credit check: 402

If your balance can't cover a new job's estimate, the API refuses it with `402 Payment Required`. It also counts
the quoted `estimated_cost` of your organization's pending and running jobs: those quotes plus the new job's own
must not exceed 10 times your balance. A job stopped by its spending cap still reserves that cap until its final
charge is settled. A 402 creates no job, so retrying the request after backing off is safe. See
[Errors](https://docs.go-fetch.io/errors.md#402-not-enough-credit) for the response and how to handle it.

The estimate is the scraper's rate times the results the job can return, counting every target: a job for 50
profiles with `limit: 100` is quoted as 5,000 results. It is in the job's `estimated_cost`. Without a limit, a target
is counted as:

* 50 posts on Instagram and Facebook;
* 1,000 videos on TikTok, or 100 with a date filter within the last 29 days;
* 100 videos on YouTube;
* one row on `instagram_profile` and `facebook_profile`.

On Instagram, Facebook, TikTok and YouTube, a job with a date filter within the last 29 days is counted at no more
than 50 results per target for each day of its window: a job for 20 channels over the last day, with a limit of
50,000, is quoted as 1,000 results. This lowers only the quote: the scraper still collects the whole window up to your
limit, you pay for the rows it delivers, and [`maxTotalChargeUsd`](#cap-a-job-with-maxtotalchargeusd) is what caps
that.

Comment scrapers count `maxComments`, TikTok search `maxItems`, TikTok profile its handles and posts, and Google
search its queries.

## Cap a job with `maxTotalChargeUsd`

Add `maxTotalChargeUsd` to a job's input (`config`): a number of USD, at least 0.01.

```bash theme={null}
curl https://go-fetch.io/api/v1/jobs/create/ \
  -H "X-API-Key: $GOFETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scraper_type": "tiktok_comments", "config": {"handles": ["duolingo"], "maxPosts": 3, "maxTotalChargeUsd": 5}}'
```

`maxTotalChargeUsd` is a ceiling on spending. It can lower a job's volume. For `tiktok_search` without `maxItems`,
the budget sets the volume, up to 100,000 items; other types never run above their volume without a budget.

1. **Split per target.** On `instagram`, `tiktok` and `facebook` the cap is split across the profiles or pages and
   written as a per-target limit (`maxPosts`, `maxVideosPerProfile`, `resultsLimit`), never above the limit you set
   or the default. A job with no limit gets one from the cap.
2. **A result cap.** On `instagram_comments` and `tiktok_comments` (`maxComments`), `tiktok_search` and
   `tiktok_profile` (`maxItems`), a cap you leave out is set to what the budget buys. `tiktok_search` is capped at
   100,000 items. An explicit `maxItems` whose cost exceeds the budget is refused with `400`; a smaller cap is kept.
3. **When it can't fit.** A job is refused with `400` only when the limit you set for one target already costs more
   than the whole budget, or the budget buys less than one result per profile. On `instagram_profile`,
   `facebook_profile` and `google_serp`, the whole job must fit the budget. The message says how many results the
   budget buys.
4. **YouTube** gets no cap written: the job is accepted with a warning in its log, and the budget is enforced while
   it runs.
5. **While it runs:** a job whose results pass 110% of the budget is stopped. Its status becomes `cancelled`, and its
   `error_message` says so. Its `actual_cost` stays `null` until the stopped run reports the rows it delivered. It
   is then billed for them, at most the budget, and its one [`job.cancelled`](https://docs.go-fetch.io/webhooks#events) webhook carries that
   charge. A run that hasn't reported 30 minutes after the stop is closed with no charge. A job its budget stopped
   can't be retried (`400`), billed or not: create a new job instead.

## Add credits

Write to [support@go-fetch.io](mailto:support@go-fetch.io) and we'll add them to your organization. Your balance is
`credit_balance` in `GET /api/v1/organizations/`.

## Low balance

When your balance falls below its warning threshold, or to zero, the organization's owners and admins get an email,
and webhooks subscribed to [`account.low_balance`](https://docs.go-fetch.io/webhooks#events) are called. Set the threshold in the dashboard's
organization settings.

## Usage

`GET /api/v1/usage/` lists one usage record per completed job: `job`, `platform`, `posts_scraped` (results billed),
`unit_price` and `total_cost`, newest first. `GET /api/v1/organizations/{id}/usage/` sums the current month.

```bash theme={null}
curl "https://go-fetch.io/api/v1/usage/?limit=10" \
  -H "X-API-Key: $GOFETCH_API_KEY"
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.