# Pricing

xskill stores customer charges as integer micro-credits:

- `1_000_000` micro-credits = `$1.00`
- `$0.02` = `20_000` micro-credits

The default card is defined in `src/pricing/engine.ts`:

- raw post: `4_000` micro-credits
- raw search: `300 * tweetsReturned`
- raw thread: `4_000 + 300 * tweetsRead`
- parsed thread: `14_000 + 300 * tweetsRead`
- premium parsed thread: `44_000 + 300 * tweetsRead`
- vision/image: `20_000 * images`

A 20-tweet raw thread prices at `10_000` micro-credits (`$0.01`), and a
20-tweet parsed thread prices at `20_000` micro-credits (`$0.02`).
A 20-tweet premium parsed thread prices at `50_000` micro-credits (`$0.05`).
A 20-result search prices at `6_000` micro-credits (`$0.006`).

The default free tier tops each account balance up to `2_000_000` micro-credits
per UTC month (`XSK_FREE_TIER_MONTHLY_MICRO_CREDITS`), which is about 100
default 20-tweet parsed-thread calls. The top-up is keyed by account and month,
not by API key, so rotating or issuing more keys does not multiply the free
pool, and unused free credits do not stack above the monthly cap.

Override the card without a code change by setting `XSK_PRICE_CARD_JSON` to a
partial JSON object. Missing fields fall back to defaults.

```json
{
  "version": "2026-07-card",
  "rawThread": {
    "baseMicroCredits": 5000,
    "perTweetMicroCredits": 250
  },
  "rawSearch": {
    "perTweetMicroCredits": 250
  },
  "parsedThread": {
    "baseMicroCredits": 15000,
    "perTweetMicroCredits": 250
  },
  "premiumParsedThread": {
    "baseMicroCredits": 44000,
    "perTweetMicroCredits": 250
  }
}
```

Usage records include the resolved price-card version, customer price,
estimated cost, and estimated gross margin.

Refund eligibility, billing disputes, and checkout policy copy live in
[`refund-policy.md`](./refund-policy.md). Signup and checkout surfaces must also
link the [`Terms of Service`](./terms.md).

Thread routes ask the active provider for the worst-case upstream tweet reads
before calling it. For twitterapi.io, `maxPages=1&maxTweets=1` reserves 20 tweet
reads because the provider bills the fetched page before xskill slices returned
tweets; if `maxPages` is omitted, the route reserves the default
5-page/100-read window and settles the debit down after the fetch.

Parsed thread requests reserve enough to cover the selected parsed price and the
raw thread fallback price. Sonnet requests use `premium_parsed_thread`; Haiku
requests use `parsed_thread`. That keeps parser failures from spending upstream
work and then discovering the raw fallback charge is higher than the reserved
debit.

Search routes reserve the provider-estimated reachable result window before
provider spend and settle the usage record to the number of tweets returned. The
default search window is one 20-tweet page; the route caps requests at 5 pages /
100 tweets.
