# 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 list timeline: `2_000 + 300 * tweetsReturned`
- raw post: `4_000` micro-credits
- raw search: `2_000 + 300 * tweetsReturned`
- raw thread: `4_000 + 300 * tweetsRead`
- raw user timeline: `2_000 + 300 * tweetsReturned`
- 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`), as do 20-tweet list and user timeline reads.

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 that always includes `version`. Missing non-version fields
fall back to defaults.

The version is a public correlation label, not a secret or free-form note. It
must match this complete grammar:

```text
^(?:\d{4}-\d{2}(?:-\d{2})?|v?\d{1,4}\.\d{1,4}\.\d{1,4})(?:[._-](?:card|default|pricing|raw|timeline|v?\d{1,4})){0,3}$
```

Accepted examples include `2026-07`, `2026-07-27`, `2026-07-card`, `1.2.3`,
and `v1.2.3-pricing`. Free-form or private labels, including `test-card`, and
labels containing whitespace are rejected.

```json
{
  "version": "2026-07-card",
  "rawThread": {
    "baseMicroCredits": 5000,
    "perTweetMicroCredits": 250
  },
  "rawList": {
    "baseMicroCredits": 2000,
    "perTweetMicroCredits": 250
  },
  "rawSearch": {
    "baseMicroCredits": 2000,
    "perTweetMicroCredits": 250
  },
  "rawUserTimeline": {
    "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.

List and user timeline routes use the same reservation and settlement pattern as
search. Successful empty reads charge the `2_000` micro-credit base, provider
failures settle back to zero, and requests are capped at 5 pages / 100 tweets.
