# 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: `2_000 + 300 * tweetsReturned`
- raw thread: `4_000 + 300 * tweetsRead`
- parsed thread: `14_000 + 300 * tweetsRead`
- premium parsed thread: `94_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 `100_000` micro-credits (`$0.10`)
when premium models are explicitly enabled. A 20-result search prices at
`8_000` micro-credits (`$0.008`).

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": {
    "baseMicroCredits": 2000,
    "perTweetMicroCredits": 250
  },
  "parsedThread": {
    "baseMicroCredits": 15000,
    "perTweetMicroCredits": 250
  },
  "premiumParsedThread": {
    "baseMicroCredits": 94000,
    "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. Haiku requests use `parsed_thread`. Sonnet requests
remain disabled unless `XSK_ENABLE_PREMIUM_MODELS=true`; when enabled, they use
the higher `premium_parsed_thread` card so worst-case parser output does not run
at a loss. The raw fallback reservation 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 plus
the fixed raw-search base. Successful empty searches therefore still charge the
`2_000` micro-credit search base; provider failures before a completed fetch
settle back to zero. The default search window is one 20-tweet page; the route
caps requests at 5 pages / 100 tweets.
