← All posts

How I cut my X API bill by ~80% (the exact playbook)

By Oualid · September 13, 2026

TL;DR: X bills the API per distinct post you read, per UTC calendar day. My bill was inflated because a background job re-read my own recent posts, including replies, every single day to refresh stats, plus a follower-count ping firing dozens of times a day. The fixes: only pull genuinely new posts with since_id, exclude replies, freeze a post's metrics after ~72 hours, throttle the follower ping, and count the daily cap in real posts read. On my own account, distinct daily reads dropped from ~500 to a small live window. Here's the whole thing.

If you're wiring the X API into a product, read this before you have users, not after. I learned it the expensive way.

How the billing actually works (this is everything)

X's 2026 API is pay-per-use. The prices that matter:

  • Post read: ~$0.005 each, charged per post returned. A call that returns 50 posts costs for 50.
  • User read: ~$0.010 each (twice a post).
  • Post created: $0.015, or $0.20 if the post body contains a URL (a ~13 to 40x jump).
  • Dedupe: per UTC calendar day. Read the same post again inside the same UTC day and you're billed once. After midnight UTC, it bills again.

Sit with that last one, because it reframes the whole problem. Your bill is the number of distinct posts you read per UTC day. Reducing repeated reads inside a day does nothing, they're already free. The only lever that moves the bill is reducing distinct reads. I spent a week optimizing the wrong thing before this clicked.

How I found the leak (instrument first)

I charged $10 to the developer account and watched ~$1.75 disappear in two days while I was doing almost nothing. I couldn't explain it, so the first move wasn't a fix, it was visibility: I logged every single API call with its endpoint and a tag for which function made it.

The log was damning. On one day my app made:

  • ~450 post-reads pulling my own recent tweets to "refresh analytics"
  • ~85 calls to /users/me just to re-check my follower count
  • ~1 call that was the actual reply-session scouting I cared about

The feature I thought was expensive (finding posts to reply to) was one call. The analytics refresh and a follower-count ping were the whole bill. You cannot fix this by intuition. Log everything for two days and the leak names itself.

Mistake 1: re-reading my own posts every day

My analytics job pulled my 50 most recent posts every time it ran, to refresh their likes and impressions. It ran many times a day, and it pulled the same posts each time. Within a UTC day those repeats were free, but across days the same ~50 posts were re-billed every single day, forever, even with zero new activity. For a user with 40 recent posts that's ~40 distinct reads a day of content that never changes.

The fix, in three parts:

  1. Only pull genuinely new posts. The user-timeline endpoint (GET /2/users/:id/tweets) supports sinceid. Store the id of the newest post you've seen, pass it as sinceid, and you get only what's new. On a quiet day that's zero reads instead of fifty. (Important gotcha: since_id works on the user-timeline endpoint but is ignored on the list-timeline endpoint. I verified this the hard way, a list fetch returned 15 posts when only 2 were actually new. Test it on your exact endpoint.)
  2. Freeze old posts. A three-week-old post's metrics barely move. So I only refresh posts from the last ~72 hours, on a taper (fresh posts more often, day-old less often), then never read them again. That caps daily analytics reads to a tiny live window instead of the whole recent history.
  3. Exclude replies. My analytics fetch used exclude=retweets only, so it was pulling my replies too, then throwing them away (the analysis ignores replies). For a reply-heavy account, that's most of the reads, paid for and discarded. Switching to exclude=replies,retweets alone is a big cut.

Mistake 2: pinging follower count on every page load

A follower-count sync was hitting /users/me on basically every app mount, 85 times on one day. The fix is boring: cache the follower count for 6 to 12 hours and serve the stored value. It doesn't need to be live to the second. (Bonus: within a UTC day these repeats were already deduped, so this saved requests more than dollars, but it's still waste and it's a rate-limit risk.)

Mistake 3: counting the daily cap in the wrong unit

I sell a "posts scouted per day" cap. The code was counting it in "accounts that produced a new post," not "posts actually read from X." So the meter could say "12 / 100" while the app had really read several hundred posts. I changed it to increment by the real number of posts each call returned, so the cap finally bounds the actual bill and matches what I advertise.

The $0.20 trap: never put a link in a post body

If your product ever publishes a post, and that post has a URL in the body, it costs $0.20 instead of $0.015, and X down-ranks it anyway. Put links in a reply, never the main post. I added a guard that warns before publishing a link-in-body post. One accidental scheduled post with a link is 13 times a normal one.

One honest caveat on the numbers

My own bill is not a clean benchmark for two reasons. First, I own the developer app, so reads of my own timeline bill at a cheaper "owned" rate; a real customer pays the standard $0.005. Second, I test all day, which inflates my usage. So model your customer at the full rate, not your own inflated-then-discounted bill.

Do-this-today checklist

  • Log every API call with an endpoint and a source tag. Watch it for 48 hours.
  • Compute your cost as distinct posts read per UTC day, not per request.
  • Add since_id so you only pull new posts. Verify it works on your specific endpoint.
  • exclude=replies,retweets anywhere you read a user's own timeline for analytics.
  • Freeze a post's metrics after ~72 hours. Stop re-reading dead posts.
  • Throttle background pings (follower count, etc.) to every 6 to 12 hours.
  • Guard against publishing a link in a post body.
  • Align heavy jobs to just after 00:00 UTC to squeeze the dedupe window.

That's the whole thing. It took a week and cut my distinct daily reads by roughly 80%. Margin is a feature. Build it early.

FAQ

How is the X API priced in 2026? Pay-per-use: ~$0.005 per post read, ~$0.010 per user read, $0.015 per post created ($0.20 with a URL), with same-UTC-day dedupe on reads.

Does re-reading the same post cost twice? Not within the same UTC calendar day, it dedupes. But it bills again the next day, which is why re-reading old posts daily is the classic leak.

Is there still a free tier? X moved to pay-per-use as the default and discontinued the old free tier. Assume you pay per read and design accordingly.