# The X API as COGS: Pricing a Product When Every User Costs You Tweets > Every price sheet quotes a per-call rate. None of them tells you what one user costs you per month. Here is the seat-level model, built from our own billed rates and our own measured page yields. - **URL:** https://www.twitterapis.com/blogs/x-api-cost-per-user-cogs-2026 - **Published:** 2026-08-31 - **Author:** Emma - **Tags:** x api cost per user, twitter api cost per user, api unit economics, cost of goods sold, saas gross margin --- > **TL;DR:** You are billed per call, not per user, so the number that decides your margin does not appear on anybody's price sheet. Write down the call graph one seat triggers in a month, multiply each call type by its billed rate, and you get a per-seat cost of goods sold you can defend. On our live rates a light seat costs $0.42 a month, a mid seat $2.73, and a heavy seat with a five minute poll $10.16. [Source: derived from the live per-endpoint price table using the call graph in this post.] At a $9 seat price that heavy seat runs at negative 12.88 percent gross margin. The poll interval, not the vendor, is the term that decides it. Every data API sells you a rate. Every finance model needs a cost. Those are different numbers, and the distance between them is where a lot of otherwise good products quietly stop working. A rate is a property of the vendor. A cost is a property of your design. The vendor publishes $0.0008 per call and stops there, correctly, because they have no idea how often you intend to call. [Source: live per-endpoint price table, origin/main f489f96b.] You publish $29 a month and stop there, also correctly, because your buyer does not care how you get the data. Somebody, at some point, has to build the bridge between those two sentences, and if nobody does it before launch it gets built afterwards by an invoice. This post is that bridge, built with our own numbers. ::directive{id="img-1"} We are going to do three things. First, look at what the live rate card actually contains, because the shape of it matters more than the headline number. Second, look at what a call actually returns, which is the measurement that turns a per-call price into a per-tweet price and is the step almost every published comparison skips. Third, build a per-seat monthly cost from a written-down call graph, put a seat price next to it, and find the point where the arithmetic stops working. Everything numeric below is either read from the live per-endpoint price table, measured from our own billing logs, or derived from those two by arithmetic we state. Where a figure is modelled rather than measured, we say so on the same line, because a modelled figure presented as a measurement is the single most common way a cost model goes wrong. ## What per-user cost means, and why it is a COGS line X API cost per user is the variable data cost your product incurs to serve one account for one billing period. It belongs on the cost of goods sold line, not the infrastructure line, because it scales with usage rather than with the size of your business. That distinction sounds like accounting pedantry until you try to raise a price, at which point it becomes the whole conversation. The classical software business has a near-zero marginal cost. You write the thing once and the ten thousandth customer costs you approximately nothing beyond a slice of a server you were already renting. Gross margins of ninety percent are normal, and every downstream assumption about growth, about hiring, about how much you can afford to spend acquiring a customer, is built on that number holding. A product whose core feature is other people's data does not have that shape. Every seat you add adds calls. Every poll you tighten adds calls. Every power user who watches forty accounts instead of five adds calls. The cost curve is not flat, and the standard [gross margin](https://en.wikipedia.org/wiki/Gross_margin) arithmetic that everybody applies to software gives you a very different answer once a real variable cost sits inside it. This is not a new observation, and it is not specific to social data. It is the same argument playing out across every product with a metered dependency underneath it: https://x.com/aakashgupta/status/2047860705228165367 The specific number in that thread is about model inference rather than social data, and the magnitude is far larger than anything discussed here. The structure is identical. A variable input cost sits between revenue and gross profit, and a pricing model designed as though it were not there produces a business that looks healthier on a revenue chart than it is on a margin chart. The practical consequence for a smaller product is less dramatic and more fixable. Nobody building a social inbox is going to lose a billion dollars. But they can very easily ship a $9 plan that loses money on its most engaged users, discover it eighteen months later, and find that fixing it means a price change on the exact customers who like the product most. [Source: derived from the live per-endpoint price table using the call graph in this post.] That is a bad position, and it is entirely avoidable with an afternoon of arithmetic before launch. The founders who get caught by this are not careless. They are usually doing the same thing everybody does, which is reading a price sheet, noting that the numbers have four decimal places, and concluding the cost is negligible. Four decimal places multiplied by a scheduled job is not negligible. That is the whole lesson, and the rest of this post is the arithmetic that makes it concrete. ## The shape of the rate card, which matters more than the headline Before you can model anything you need to know what you are actually buying. The instinct is to look for the headline rate and use it everywhere. That works better than you might expect on our surface, but only because of a property of the price table that is worth reading directly rather than assuming. ::directive{id="dt-tier-census"} ::directive{id="img-2"} Eighty percent of the priced endpoints sit at a single standard rate. That is the useful fact. It means that for most call graphs you can model with one number and be close, then correct for the handful of premium calls you actually make. It also means that when your bill surprises you, the surprise is almost never a tier you did not know about. It is a volume you did not count. The premium rows are worth knowing individually because each one has a distinct reason for being priced above standard, and the reason tells you when to reach for it. Tweet creation, DM send and the two DM reads sit at double the standard rate. Full account history pagination sits at three times. Full thread expansion and Grok chat sit at five times, priced that way for the wall-clock cost of holding a long connection open and the upstream work behind it rather than for the volume of data returned. Account login sits highest of all and is billed only on success, which matters if you are retrying a flaky connection flow. There is a design implication hiding in that list. The expensive calls are the ones that do a lot of work on your behalf in a single request. A thread expansion is five standard calls in price and replaces an unknown number of manual pagination calls plus the code to stitch them together. Whether it is a saving depends on how deep the threads you care about actually are, which is a measurement you can take rather than a guess you have to make. The same logic applies to full history pagination. It costs three times the standard read and exists because walking an account back to its first post is a genuinely different operation from reading a recent page. If your product needs a one-time backfill on signup, it is the right call and it appears exactly once per account. If your product is calling it on a schedule, something has gone wrong upstream in the design. For the full route-level detail behind these tiers, our [complete endpoint reference](/blogs/twitter-api-reference) enumerates what each one returns, and the [pricing walkthrough](/blogs/twitter-api-cost) covers the mechanics of how billing is recorded. What follows assumes you have picked your endpoints already, and asks what they cost when a real product calls them on a real schedule. ## How many tweets does one call actually return? One call returns a variable number of tweets, not a fixed page, and that variability is the step which separates a cost model that survives contact with an invoice from one that does not. You are billed per call and you care about tweets, so the conversion between those two units is a measurement rather than a constant, and it differs by endpoint. You are billed per call. You care about tweets. The conversion between those two is the number of tweets a call returns, and it is not a constant. It is a distribution, it varies by endpoint, and a call that comes back empty costs exactly the same as a call that comes back full. ::directive{id="dt-yield"} ::directive{id="img-3"} Those are our own billing logs, not an estimate. Across 396,817 successful tweet-returning read calls in a five day window, timeline reads averaged 18.78 tweets per call, all bulk reads excluding single-tweet detail averaged 12.96, and advanced search averaged 7.62. [Source: biller usage logs, n=396817 calls, 2026-08-13 to 2026-08-17.] Nearly thirty percent of those search calls returned nothing at all. Sit with the last figure for a moment, because it is the one that changes designs. If close to a third of your search calls return zero rows, and each of those zero-row calls bills at the full standard rate, then a naive per-thousand-tweets estimate built on a full page is understating your search cost by more than a factor of two. Not by a rounding error. By 2.6 times. This is also why we publish a per-1,000-tweets figure with its basis stated rather than as a bare price. The often-quoted $0.04 per thousand is the standard call rate spread over a full twenty tweet page. [Source: live per-endpoint price table, origin/main f489f96b.] Twenty is the default page size the client requests, clamped between one and one hundred. It is a ceiling the caller can raise or lower, and it is not a guaranteed yield. Timeline reads land close to it. Search reads frequently do not. There is a wider point here about how to read anybody's data pricing, ours included. Any per-unit figure that is not the unit you are billed in is a model. It has assumptions inside it. Those assumptions may be reasonable, and they may even be stated, but they are not a price and they will not appear on your invoice. The contractual number is the per call rate. Everything downstream of it is arithmetic you should do yourself, with your own yields, from your own logs. If you are choosing between providers, this is the comparison that actually discriminates between them, and it is almost never the one the comparison pages run. Our [provider cost benchmark](/blogs/twitter-api-cost-benchmark-2026) works through the like-for-like version, and the [per-1,000-tweet ranking](/blogs/cheapest-twitter-api-2026-8-providers-ranked-by-real-per-1000-tweet-cost) shows how far the headline figures drift from the real ones once yield is included. The practical instruction is short. Before you model anything, run your intended query shape a few hundred times and record how many rows come back. It costs a few cents and it replaces the single largest source of error in the whole exercise. If you cannot do that yet, use a yield you can defend and label it as an assumption in the model, so that when the invoice disagrees you know which term to correct. ## Building the call graph, which is the only real work Everything so far is inputs. The model itself is straightforward and mostly clerical, which is why it so often does not get done. There is no clever step. There is just a list, and somebody has to write it. ::directive{id="img-4"} The list has five columns. What triggers the call. How often it happens per seat per month. Which endpoint it hits. What that endpoint bills. The product of the last two. The discipline is in the first two columns, not the last two. The rates are published and the arithmetic is trivial. What is genuinely hard is enumerating every trigger, because a real product has more of them than anybody remembers. Scheduled jobs are the easy half. A poll runs on a cron, its frequency is a configuration value, and you can read it off. Multiply and move on. These are the calls that dominate most cost models and they are also the ones you have most control over. User actions are harder. Somebody opens a thread and you expand it. Somebody adds an account to a watchlist and you backfill it. Somebody clicks refresh because the page felt stale. Each of those is a call, none of them is on a schedule, and the frequency has to come from your product analytics rather than from a config file. If you do not have that data yet, put in a number and mark it as an estimate, because an estimated row in a model that gets updated beats a missing row in a model that looks complete. Then there is the third category, which is the one that gets missed and is disproportionately expensive: calls nobody wrote deliberately. Retries are the classic case. A transient failure gets retried three times, each retry bills, and the retry logic lives in a shared HTTP client that nobody has read in a year. Pagination is another. A feature that fetches "the user's recent posts" may be three calls or thirty depending on how the loop terminates. Background reconciliation jobs are a third. Something runs nightly to keep a cache warm, it was added by somebody who has since moved teams, and it is quietly the second largest line in your bill. There is only one reliable way to find these, and it is not code review. Log every outbound call with the account it belongs to, then look at the distribution. Everything you did not expect shows up immediately, and everything you did expect gets its assumed frequency replaced with a measured one. ```python # Log every call at the HTTP client, not at the feature. # Attribution added per-feature misses retries, background jobs and # anything a shared helper calls, which is where the surprises live. import os, time, requests BASE = "https://api.twitterapis.com/twitter" RATES = { # read from the live price table "user/mentions": 0.0008, "user/tweets": 0.0008, "tweet/detail": 0.0008, "tweet/thread": 0.0040, "tweet/create": 0.0016, "user/tweets/complete": 0.0024, } def call(endpoint, account_id, **params): started = time.time() r = requests.get( f"{BASE}/{endpoint}", headers={"x-api-key": os.environ["TWITTERAPIS_KEY"]}, params=params, timeout=30, ) rows = len((r.json() or {}).get("tweets") or []) if r.ok else 0 usage_log.write( account_id=account_id, endpoint=endpoint, billed_usd=RATES.get(endpoint, 0.0008), rows_returned=rows, # this is what gives you real yield ok=r.ok, latency_ms=int((time.time() - started) * 1000), ) r.raise_for_status() return r.json() ``` Two details in that snippet are load-bearing and easy to leave out. The first is `rows_returned`. Without it you can compute cost per account but not cost per tweet, which means you can never check your yield assumption against reality. It is one integer and it is the difference between a model you trust and a model you hope about. The second is logging on failure as well as success. A call that errored still cost you, unless the vendor explicitly does not bill it, and the failure rate is exactly the kind of thing that drifts upward slowly without anybody noticing. If your logging is inside the success branch, your model will be optimistic by however much your error rate is, and it will get more optimistic as things get worse. ## Five seat archetypes, priced With a call graph you can price a seat. Here is a worked one for a social inbox product, which is a common enough shape to be useful and specific enough to be checkable. Replace the frequencies with your own; the method is the transferable part, not the numbers. ::directive{id="dt-seat-cogs"} ::directive{id="img-5"} The distribution is the finding. One line, the mentions poll, dominates everything else by roughly an order of magnitude. The competitor tracker, the engagement refresh, the thread reader, the scheduler, the backfill and the login together come to well under a dollar. The five minute poll on its own is $6.91. [Source: derived from the live per-endpoint price table using the call graph in this post.] That is not a quirk of this example. It is structural, and it follows from the arithmetic of scheduling. A poll is the only line whose frequency is not bounded by human behaviour. A user reads some number of threads a month because there are only so many hours in a day. A poll runs on a clock and does not get tired. Multiply anything by 8,640 and it stops being small. ::directive{id="cb-seat-cost-lines"} Grouping the rows gives three archetypes worth naming. A light seat is the tracker, the thread reader and the scheduler, with no continuous polling at all. Refresh happens when the user opens the app. That comes to $0.4240 a month, and at any seat price above about $5 you have a business with software margins. A mid seat adds a fifteen minute mentions poll, which is fast enough that most users will never notice the delay. That comes to $2.7280. Still comfortable at $19 and up, and it is the shape most products should probably ship first. [Source: derived from the live per-endpoint price table using the call graph in this post.] A heavy seat runs the five minute poll and everything else in the table. That comes to $10.1588, and it is where the interesting decisions live. [Source: derived from the live per-endpoint price table using the call graph in this post.] ::directive{id="pm-seat-cost-panel"} Notice what the heavy seat buys for its extra spend. The gap between fifteen minutes and five minutes is $4.61 a month per seat. In exchange, alert latency drops by an average of five minutes. For a marketing team watching brand mentions that is worth approximately nothing. For a support team with a response-time commitment, or a trading desk, or anyone whose alert triggers a human action with a clock on it, it may be the entire product. That is a pricing question rather than an engineering one, and it has a clean answer: if the fast poll is worth paying for, it belongs on a higher plan, priced with the cost in view. If it is not worth paying for, it should not be the default. What it should never be is a free default on your cheapest plan, which is exactly where it usually ends up, because polling faster feels like a quality improvement rather than a cost decision. ## At what seat price does the arithmetic stop working? It stops working at whatever price sits below the seat's monthly data cost divided by the margin you need, which on the heavy archetype modelled here is somewhere between nine and nineteen dollars. Put a seat price next to each archetype and the picture resolves immediately, because gross margin is just the seat price minus the data cost over the seat price. ::directive{id="dt-margin"} ::directive{id="img-6"} The light seat is a software business at every price point. Even at $9 it holds 95.29 percent, and nothing you do to it short of a hundredfold volume increase will change that. [Source: derived from the live per-endpoint price table using the call graph in this post.] If your product can be built this way, build it this way. The mid seat is comfortable from $19 upward and gets tight below it. At $9 it is 69.69 percent, which is survivable but is no longer a software margin, [Source: derived from the live per-endpoint price table using the call graph in this post.] and it leaves considerably less room for the rest of your cost base than your board deck probably assumes. The heavy seat at $9 is negative 12.88 percent. Every one of those customers costs you $1.16 a month to serve, [Source: derived from the live per-endpoint price table using the call graph in this post.] before compute, before storage, before payment processing, and before support. Growth on that plan makes the company smaller. ::directive{id="img-11"} We should be precise about what that number is and is not. It is a data-cost-only margin on a modelled call graph. It excludes everything else you pay for, so a real business is further underwater than this table shows, not less. And the call graph is a worked example rather than a measurement of any customer, which is why the whole point of the exercise is to substitute your own frequencies. What generalises is not the numbers. It is the observation that a heavy seat and a light seat on the same plan are two different businesses wearing the same price tag, and that nothing in a standard revenue dashboard will ever tell you which one you have more of. This is the point of the whole exercise, and it is not a hypothetical worry. It is the most commonly reported failure mode among founders who have shipped a usage-backed product and then had to unwind the pricing: https://www.reddit.com/r/SaaS/comments/1peunn2/just_watched_a_founder_hit_50k_mrr_with_a_72k_aws/ The thread is about compute rather than social data, and the numbers are much larger, but the failure is the same and so is the sequence. Ship an unlimited plan because it reduces friction. Watch it work. Acquire a power user. Discover that the power user is the loss. Try to reprice and find that the people most affected are the people who like the product most, which makes the change land as a betrayal rather than a correction. Founders who model this before shipping rather than after tend to reach the same conclusion, and the arithmetic is usually the thing that convinces them: https://www.reddit.com/r/SaaS/comments/1t9wi3j/the_ai_cost_cliff_for_mobile_apps_i_did_the_math/ The version of this that is specific to data APIs has one additional trap. Compute costs at least show up on a dashboard that somebody watches. A data API bill arrives monthly as a single number with no per-account breakdown unless you built one, so the discovery step takes longer and the hole is deeper by the time anyone looks. ## The poll interval is your margin dial If the poll dominates the cost, then the poll interval is the control surface, and it is worth inverting the question. Rather than asking what a given interval costs, ask what interval a given margin target can afford. ::directive{id="img-7"} Take a $29 seat and a target of eighty percent gross margin on data. [Source: derived from the live per-endpoint price table using the call graph in this post.] That leaves $5.80 a month for data, which at the standard rate is 7,250 calls, which is 242 calls a day, which is one call every 5.96 minutes. [Source: derived from the live per-endpoint price table using the call graph in this post.] The same arithmetic at other price points: a $9 seat affords one call every 19.20 minutes, a $19 seat one every 9.09 minutes, and a $49 seat one every 3.53 minutes. [Source: derived from the live per-endpoint price table using the call graph in this post.] ::directive{id="img-13"} ```python # What poll interval can a seat price afford at a target margin? READ_RATE = 0.0008 # standard per-call rate, live price table def affordable_interval_minutes(seat_price, target_margin=0.80, pollers_per_seat=1): budget_usd = seat_price * (1 - target_margin) calls_per_month = budget_usd / READ_RATE calls_per_day = calls_per_month / 30 / pollers_per_seat return 1440 / calls_per_day for price in (9, 19, 29, 49): mins = affordable_interval_minutes(price) print(f"${price:>3}/seat -> one call every {mins:.2f} min") ``` Three things about that calculation are worth saying out loud, because each one has bitten a real product. `pollers_per_seat` is not always one. If a seat can watch five accounts and each gets its own poller, the affordable interval multiplies by five. A product that lets a user add accounts without adding cost has an unbounded call graph wearing a bounded price, which is the same failure as an unlimited plan with a different shape. Cap the watchlist, or meter it. Eighty percent is a choice, not a law. It is a reasonable target for a product that also has compute, storage, support and sales to fund out of the same gross profit. If your data cost is your only significant variable cost, you can afford to spend more of the seat on it and get a better product. State the target explicitly rather than letting it emerge from whatever interval somebody typed into a cron. And the seat price is an input, not an output. Working backwards from the interval the product needs to a seat price that supports it is a legitimate and often better direction to run the calculation. If your product genuinely requires a thirty second poll, that is a fact about the product, and the correct response is to price it accordingly rather than to ship it at $9 and hope. [Source: derived from the live per-endpoint price table using the call graph in this post.] The deeper design move, once you have this number in front of you, is usually to stop treating the poll interval as a global constant. Poll the accounts that are actually busy more often than the ones that are not. Back off when an account has been quiet. Poll harder during the hours a customer's audience is awake. Each of those is a straightforward change that buys real latency where it matters and gives it back where it does not, and none of them is available to you until somebody has written the interval down as a cost. ## What does the free signup credit actually buy? Every signup on our side comes with a $0.50 credit and no card. [Source: live per-endpoint price table, origin/main f489f96b.] It is worth being precise about what that covers, because a free tier that runs out mid-evaluation is worse for everybody than one that is honestly described. ::directive{id="dt-freecredit"} ::directive{id="img-8"} The credit divided by the standard rate is 625 calls. What that means in tweets depends entirely on which path you take, which is the same yield lesson from earlier arriving in a place people actually feel it. On timeline reads that lands at 11,738 tweets. On search reads, where nearly a third of calls return nothing, it lands at 4,762. For its purpose that is generous. You can build the whole call graph, run it end to end, measure your real yield on your real queries, and have the numbers you need to fill in the model in this post. That is a much better use of a free tier than an arbitrary rate limit that lets you make one call a minute forever without ever learning anything. What it does not cover is production. A single fifteen minute poller burns 96 calls a day, so it consumes the entire credit in about six and a half days. A five minute poller does it in a little over two. That is not a criticism of the credit, it is the arithmetic of continuous polling, and it is the same reason the poll dominates the seat cost table above. Our [free tier walkthrough](/blogs/is-twitter-api-free) covers what is and is not available without a card across the market, and the [key setup guide](/blogs/how-to-get-twitter-api-key) covers getting from signup to a first successful call. The [authentication guide](/blogs/twitter-api-authentication) covers the credential model, which matters for the write endpoints in the seat table above. ## Reading the official rate card next to your own model Anyone doing this exercise seriously will want to run the same model against the official platform path as well, and the comparison turns out to be instructive for reasons that have very little to do with the headline per-call numbers and a great deal to do with which capabilities are guaranteed to still exist next quarter. The [official X API](https://docs.x.com/x-api/introduction) moved to consumption-based billing, replacing the old fixed monthly tiers with credits you purchase and draw down. The [platform overview](https://docs.x.com/x-api/getting-started/about-x-api) sets out how those credits are purchased and drawn down. The direction of travel is the same one this whole post assumes: you are billed for what you consume, so what you consume is a design decision. The rate changes announced to developers through 2026 are worth reading in full, and they were widely circulated at the time: https://x.com/Xclusiv/status/2045562091273077036 Two structural points in that announcement matter more than the specific figures, and both are the kind of thing a cost model needs to be robust against. The first is that reads of your own data were priced differently from reads of everybody else's. That is a sensible distinction and it has a real consequence for modelling: if your product reads the customer's own account, your per-user cost is on a different curve from a product that reads the open firehose, and comparing the two as though they were the same workload will mislead you in both directions. The second, and more important, is that some write actions were removed from self-serve tiers entirely. A rate can be modelled. A capability that disappears cannot. This is the risk that no spreadsheet catches, and it is the strongest practical argument for keeping your call graph written down somewhere a human can read: when a platform changes what is available, the first question is which lines of your model just became zero, and you can only answer that quickly if the lines exist. We keep a running account of these changes in the [X API pricing change log](/blogs/x-api-pricing-change-2026), and the [official versus third party decision guide](/blogs/official-x-api-vs-third-party-2026) works through when each path is the right one. If you are weighing the two, the [v2 comparison](/blogs/twitter-api-v2-vs-twitterapis) is the endpoint-level version of that question. For general reading on how usage-based billing is implemented once you have decided to meter something, the [Stripe usage-based billing documentation](https://docs.stripe.com/billing/subscriptions/usage-based) is the clearest practical treatment, and their [pricing models reference](https://docs.stripe.com/billing/subscriptions/usage-based/pricing-models) covers the graduated and volume shapes most products end up wanting. The accounting definition of [cost of goods sold](https://en.wikipedia.org/wiki/Cost_of_goods_sold) is worth a skim if you are about to argue with a finance function about which line this belongs on. ## How do you attribute API spend to a specific user? A model built once and never checked is a guess with a spreadsheet around it. The thing that turns it into an instrument is per-account attribution, and the reason to build it early is that it is trivial before you have traffic and awkward afterwards. The requirement is one row per outbound call, carrying the account it belongs to, the endpoint, the billed rate, whether it succeeded, and how many rows came back. That is five columns and it answers every question you will have for the next two years. ::directive{id="img-12"} ```sql -- Cost per account for a billing period, straight from the call log. -- The percentile columns are the point: the mean will lie to you, -- because this distribution always has a long right tail. SELECT account_id, COUNT(*) AS calls, SUM(billed_usd) AS data_cogs_usd, SUM(rows_returned) AS rows_returned, SUM(billed_usd) / NULLIF(SUM(rows_returned), 0) * 1000 AS usd_per_1k_rows, SUM(CASE WHEN NOT ok THEN billed_usd ELSE 0 END) AS wasted_on_errors_usd FROM usage_log WHERE called_at >= date_trunc('month', now()) GROUP BY account_id ORDER BY data_cogs_usd DESC LIMIT 50; ``` Run that on the first day of a month and the second week and you will learn three things you cannot learn any other way. You learn the shape of the distribution. The mean cost per account is close to useless because this distribution is never normal. There is a long right tail, that tail is where your margin risk lives, and a single average hides it completely. Look at the ninety fifth percentile and the maximum, and compare each of them to your seat price. If the maximum is above the seat price you have a customer you are paying to keep. You learn your real yield, per endpoint, on your real queries. That replaces the assumption in the model with a measurement, which is the single highest-value correction available to you. Our numbers are ours. Your queries are narrower or broader than ours, and your yield will differ. You learn what you are spending on failures. A call that errors still bills unless the vendor says otherwise, and error rates drift upward quietly. Putting the wasted spend in its own column turns a slow degradation into a number somebody can watch. There is an organisational point here too, which matters more than the technical one. The moment cost per account exists as a queryable number, the conversation about pricing changes character. It stops being an argument between an engineer who thinks it is fine and a founder who is worried, and becomes a fact that both of them can read. Every product we have seen handle this well built the number before they needed it. Every product that handled it badly built the number in a panic, after an invoice, with a customer conversation already scheduled. The practitioners asking this question in public are almost always asking it too late, once the bill is already confusing: https://www.reddit.com/r/SaaS/comments/1w2fmji/how_are_you_tracking_api_costs_and_earnings_per/ The answers in threads like that converge on the same conclusion every time. A provider dashboard tells you what you spent. It cannot tell you who you spent it on, because the provider has never heard of your users. That mapping only exists if you write it down at the moment of the call, and no amount of after-the-fact reconciliation will reconstruct it. If you want the operational version of this, our [best practices guide](/blogs/twitterapis-best-practices) covers client-side patterns including retry and backoff, and the [rate limit guide](/blogs/twitter-api-rate-limit-guide) covers what happens at the boundaries. Both are worth reading before you set your retry policy, because retries are the line item that most often turns out larger than expected. ## Should you charge per seat, per credit, or both? Charge per seat for the part of the call graph you control, meter the part the customer controls, and in most products that means both. A product like this has two genuinely different cost shapes inside it, and nearly every pricing failure we have seen comes from applying a single plan structure to both of them at once. ::directive{id="cg-plan-shapes"} ::directive{id="img-9"} The first shape is bounded and predictable. A poll interval you set. A watchlist with a cap. A refresh schedule you control. You can compute this cost exactly, before the customer arrives, because every term in it is a number in your own configuration. A flat seat price fits it perfectly, and trying to meter it just adds friction and support load for no benefit. The second shape is unbounded and user-driven. An export. A backfill. A bulk enrichment run. A historical query across a wide date range. You cannot compute this in advance because the customer decides the volume, and the ratio between a median user and a heavy one is not two to one, it is often fifty to one. A flat price on this shape is a bet that nobody will use the feature much, and that bet is lost by exactly the customers you most want to keep. ::directive{id="img-14"} Almost every product that survives this ends up hybrid, and the hybrid is not a compromise. It is the structure matching the reality: a seat price that covers a modelled baseline of bounded work, plus metered credits for the unbounded actions, plus a hard ceiling that fails loudly rather than spending silently. Three implementation details decide whether a hybrid works in practice. The ceiling has to be real. A soft warning that emails somebody is not a ceiling, it is a notification about a thing that already happened. The check belongs in the code path before the call, and hitting it should return a clear error that names the limit and how to raise it. A silent overrun discovered on an invoice is the failure this whole post exists to prevent. ::directive{id="img-15"} The unit has to be something the buyer already understands. Selling "credits" that map to your internal call count means every customer has to learn your cost structure to predict their bill, which they will not do. Sell the unit they think in: accounts tracked, exports run, posts analysed. Do the conversion to calls yourself, on your side, with margin included. And the baseline in the seat price has to be generous enough that most customers never think about metering at all. If half your users hit the meter, you have not built a hybrid, you have built a metered product with a confusing entry fee. The meter is there for the tail, not the median. For the shape of this at the API layer rather than the plan layer, our [pay-per-use pricing model](/blogs/twitter-api-cost) is the worked version of the same idea, and the [build versus buy analysis](/blogs/build-vs-buy-twitter-x-monitoring-tool-2026) covers the case where the metered dependency is the reason to buy rather than build in the first place. ## How does a per-seat cost model go wrong in practice? It goes wrong in six recurring ways, and each one has a distinct early symptom that is easy to read once you know to look for it. Everything above is the model working, so it is worth naming the specific failures directly, because none of the six announces itself and several look like success while they are happening. **The unlimited plan.** Shipped for a good reason, which is that friction kills conversion, and every one of them works beautifully until the first heavy user. The symptom is a support conversation where somebody enthusiastically describes how much they use the product and your first internal reaction is dread rather than pleasure. If you notice yourself hoping customers use the product less, the plan is already wrong. **The unbounded watchlist.** A seat price computed for five tracked accounts, and a UI with an add button and no limit. The cost per seat is now a function of customer behaviour rather than of your configuration, and the cap you did not set is the one you will set later, in an email, to people who already built workflows on top of not having one. **The backfill on signup.** A one-time cost that feels free because it is one-time. It is one-time per account, which means it scales with signups rather than with revenue, and it lands hardest exactly when you are running a growth campaign. Worse, it happens before you know whether the account will convert, so a free trial with a deep backfill is a marketing spend that nobody has categorised as one. **The retry storm.** An upstream hiccup, a retry policy with no backoff and no cap, and a bill three times its normal size for a day nobody logged. The fix is standard client hygiene, and the reason it gets missed is that retries are invisible in feature-level analytics. They only exist in the call log, which is the argument for having one. **The forgotten job.** Something scheduled a year ago by somebody who has since moved on. It runs nightly, it warms a cache nothing reads anymore, and it is the second largest line in the bill. Every estate over about two years old has one. The only way to find it is to group your call log by endpoint and ask what each one is for. **The averaged model.** The most dangerous of the six because it looks like diligence. Total spend divided by total accounts gives a number that is correct, defensible and useless, because it describes a customer who does not exist. The median user and the ninety fifth percentile user are different businesses. Model both, and price for the second. ::directive{id="img-16"} The pattern connecting all six is that none is detectable from revenue, and none is detectable from a provider dashboard. Every single one is obvious within about ten minutes of looking at a per-account call log, which is why that log is the highest-leverage thing in this entire post. For a broader view of how usage-linked cost of goods sold behaves as a product scales, this walkthrough covers the accounting framing carefully and is worth the five minutes: https://www.youtube.com/watch?v=hWX_PX8oJO8 ## Objections worth taking seriously **"Our data cost is a rounding error, this is over-engineering."** Very possibly, and if the light seat archetype describes your product then it genuinely is. The test is not whether the cost is small today, it is whether the mechanism that generates it is bounded. A small cost with an unbounded mechanism is a large cost that has not happened yet. Do the arithmetic once, confirm it is small, write down why, and stop. That takes an afternoon and it is the whole ask. **"We will just raise prices if it becomes a problem."** You can, and it will be considerably harder than it sounds, because the customers affected are the heaviest users and they are also your best references, your loudest advocates and the ones whose workflows are most entangled with the product. Repricing them is not a spreadsheet operation, it is a relationship operation, and it costs goodwill you spent years accumulating. Getting the structure approximately right at launch is cheaper by an enormous margin. **"Caching solves this."** It solves a real and often large part of it. Profile lookups, follower counts, tweet detail on posts you have already seen: cache all of it, aggressively, and your cost drops meaningfully. What caching cannot touch is the poll, because the entire purpose of the poll is to discover something you have not seen, and a cache hit there means you did not look. Since the poll is usually the dominant line, caching improves the model without changing its shape. **"We are on a fixed-price plan so none of this applies."** It applies with one substitution. Your marginal cost is zero until you hit the ceiling, at which point it becomes the cost of the next tier divided by the headroom it buys, which is a step function rather than a line. Step functions are harder to reason about, not easier, and the seat that pushes you over a tier boundary costs you the entire increment. Model the headroom per seat instead of the cost per seat, and the rest of this post transfers directly. **"Our provider might change prices anyway."** True, and it is an argument for having the model rather than against it. A model with a rate as a named input lets you answer "what happens if this doubles" in ten seconds. Without it, a price change becomes a research project run under time pressure, usually by the person least able to spare the week. The rate changing is exactly the event the model exists for. ## The pre-launch checklist Nine questions decide whether a data-backed price will hold, and every one of them is answerable in a single focused afternoon before launch. All nine are dramatically more expensive to answer afterwards, because by then the answers are constrained by customers who already bought the plan you are trying to change. ::directive{id="img-10"} **1. What is the per-seat call graph, written down?** Not estimated, written. A list with a trigger, a frequency, an endpoint and a rate. If you cannot produce it, you do not have a cost model, you have an intuition. **2. What is the poll interval, and what did it cost to choose it?** If nobody can say why it is the number it is, it was chosen by whoever typed the cron line. Since it usually dominates the bill, it deserves an actual decision. **3. Is the watchlist capped?** If a user can add tracked accounts without limit, your per-seat cost is unbounded and the flat price is a bet. **4. What does the ninety fifth percentile user cost?** Not the mean. If you cannot answer this, you have no visibility into the accounts that decide your margin. **5. Does a signup trigger a backfill, and how deep?** This cost scales with signups rather than revenue, and it is incurred before conversion. **6. What is your retry policy, and is it capped?** Retries bill. Uncapped retries under an upstream incident produce a bill nobody planned. **7. Is there a hard ceiling in the code path?** Not a warning email. A check before the call that returns an error naming the limit. **8. Can you attribute spend to an account today?** If not, build the log before you need it. It takes an hour now and a week later. **9. What happens if the rate doubles?** If the answer requires new work rather than changing one number, the model is not a model yet. Working through those nine is not a large project. It is one focused afternoon, and it converts the largest unknown in a data-backed product into a number you can put in front of anybody. ## A worked month for a forty seat account Per-seat figures are the right unit for a pricing decision and the wrong unit for a finance conversation, so here is one month at a stated account size with every term visible. The account: forty seats on a $29 plan, which is $1,160 of monthly recurring revenue. [Source: derived from the live per-endpoint price table using the call graph in this post.] Twenty five of those seats are mid-weight, running a fifteen minute mentions poll. Ten are light, refreshing only on open. Five are heavy, on the five minute poll. That distribution is chosen because it is roughly what a real team looks like, with a small group of power users and a long tail who barely log in. **The mid seats.** Twenty five at $2.7280 is $68.20 for the month [Source: derived from the live per-endpoint price table using the call graph in this post.]. **The light seats.** Ten at $0.4240 is $4.24 [Source: derived from the live per-endpoint price table using the call graph in this post.]. **The heavy seats.** Five at $10.1588 is $50.79 [Source: derived from the live per-endpoint price table using the call graph in this post.]. **The account total** is $123.23 of data cost against $1,160 of revenue, which is a gross margin of 89.38 percent on data alone [Source: derived from the live per-endpoint price table using the call graph in this post.]. That is a healthy number and it is the number most people would stop at. Look at the composition instead. Five seats out of forty, twelve and a half percent of the account, produce $50.79 of the $123.23, which is 41 percent of the cost [Source: derived from the live per-endpoint price table using the call graph in this post.]. The ten light seats produce 3 percent of it. If the account renews with fifteen heavy seats instead of five, the data cost goes to $224.20 and the margin falls to 80.67 percent without a single new customer being acquired or a single price changing [Source: derived from the live per-endpoint price table using the call graph in this post.]. That is the mechanism nobody sees on a revenue dashboard: an account can get materially less profitable while its revenue stays exactly the same, purely because the mix of how people use it shifted. Nothing in your billing system reports that, and nothing in your product analytics reports it either, because analytics counts sessions and features rather than calls. Two practical consequences follow. The first is that account-level margin is worth computing, not just company-level. The company number here is 89.38 percent and it is comfortable. The heavy-seat number inside it is 64.97 percent, and if the account were entirely heavy seats it would be $406.35 against $1,160, or 64.97 percent [Source: derived from the live per-endpoint price table using the call graph in this post.], which is a different business with the same logo on the invoice. The second is that the upgrade path should follow the cost. If the five minute poll is what heavy users want, and it costs $7.43 a month more per seat than the fifteen minute version, then a plan tier priced ten or fifteen dollars above the base recovers it comfortably and lets the customers who need it self-select [Source: derived from the live per-endpoint price table using the call graph in this post.]. Nothing about that is aggressive pricing. It is charging for the thing that costs money, which is the only pricing rule that survives contact with a growing account. ## Three product shapes, three different answers The same model produces very different conclusions depending on what the product is, and walking three of them through end to end is more useful than any amount of general advice. **A brand monitoring tool sold to marketing teams.** The buyer wants to know when their brand is mentioned and does not have a hard latency requirement, because the response to a mention is a human writing a reply during working hours. The correct design is a fifteen minute poll, or an even slower one overnight, with a generous watchlist because a marketing team tracks competitors and campaign hashtags as well as the brand. Seat cost lands near the mid archetype. At $29 a seat this holds a comfortable margin, [Source: derived from the live per-endpoint price table using the call graph in this post.] and the money is better spent on the reporting layer than on latency nobody will notice. The failure mode to guard against here is watchlist growth rather than poll frequency, because marketing teams add things and never remove them. **A support inbox that routes public mentions into a ticketing queue.** Now latency matters, because the customer has a stated response-time commitment and every minute of poll delay eats into it. The five minute poll is not a luxury, it is the product, and the heavy archetype is the honest model. That means the seat has to be priced above $19 to work at all and realistically above $29 to leave room for everything else the business pays for. [Source: derived from the live per-endpoint price table using the call graph in this post.] The right move is to say so plainly on the pricing page: fast alerting is a paid capability, here is the tier it lives on. Customers who need it will pay, and the ones who do not get a cheaper product that still works. **A research or dataset product sold to analysts.** Almost none of the cost is polling. It is bulk reads, deep history and search, all triggered by a human deciding to run something. The unbounded share of the call graph is close to one, which means a flat seat price is not a price at all, it is a lottery ticket. This product should be metered from day one, in a unit the analyst thinks in, with a visible balance and a hard ceiling. Trying to sell it as a flat subscription is the single most reliable way to build a business whose best customers are its worst accounts, and the search yield measurement from earlier matters more here than anywhere else, because search is the dominant call type and it has the worst and most variable return per call. The common thread is that the API is the same in all three cases and the correct pricing structure is different in all three. Nothing about the vendor decides it. The call graph decides it, and the call graph is a consequence of what the product promises. That is worth stating plainly because it cuts against the way these decisions usually get made. The conversation normally starts with which provider to use, treats price per call as the deciding variable, and never gets to the design question underneath. In practice the provider choice moves your cost by some percentage. The design choices in this post move it by multiples. A team that picks the second-cheapest provider and polls every fifteen minutes will comfortably beat a team that picks the cheapest and polls every minute, and no comparison table will ever tell them that. ## What we would actually do If we were pricing a new product on this data today the sequence would be short, and it would start from the cheapest possible call graph rather than from the most capable one. The order matters more than any individual decision, because each step buys information the next step needs. Start with the light archetype. Refresh on open, no continuous polling, generous caching. It costs cents per seat, it holds software margins at any plan price, and it lets you learn what customers actually want before you spend anything on latency they may not value. Instrument from the first call. The per-account log costs an hour to build on day one and it is the input to every subsequent decision. Include `rows_returned` and log failures. Ship the fast poll as a paid upgrade rather than a default. Price it with the $4.61 monthly delta from the seat table in view, [Source: derived from the live per-endpoint price table using the call graph in this post.] and let the customers who need five minute latency tell you so by paying for it. The ones who do not need it get a cheaper product and you get a healthier one. Cap the unbounded things, all of them, from the beginning. Watchlist size, export volume, backfill depth. A cap that is generous enough to be invisible to ninety five percent of users is not a limitation, it is the thing that lets you offer the other five percent a plan that works for both of you. And re-run the model quarterly against the measured numbers rather than the assumed ones. Yields drift. Query shapes drift. Retry rates drift. The model is a living instrument or it is a document, and only one of those is useful. The underlying point is simple enough to state in a sentence. In a product whose cost scales with data, the pricing page and the cron schedule are the same document written in two different languages, and the business works exactly to the extent that somebody has read both. ## A reference implementation of the model The model in this post is small enough to be a single file, and keeping it as code rather than as a spreadsheet has one large advantage: it lives in the repository next to the thing it describes, so a change to the poll interval and a change to the cost model can happen in the same commit and be reviewed together. ```python """Per-seat cost of goods sold for a data-backed product. Rates are the LIVE per-call prices. Frequencies are yours. Keep this file next to the scheduler config so a change to one is visibly a change to the other. """ from dataclasses import dataclass READ, CREATE, COMPLETE, THREAD, LOGIN = 0.0008, 0.0016, 0.0024, 0.0040, 0.0100 @dataclass(frozen=True) class Job: name: str calls_per_month: float rate: float bounded_by: str # "config" or "user", and this field is the point @property def monthly_usd(self) -> float: return self.calls_per_month * self.rate SEAT = [ Job("mentions poll 15m", 4 * 24 * 30, READ, "config"), Job("competitor sweep", 5 * 30, READ, "config"), Job("engagement refresh", 20 * 30, READ, "config"), Job("thread expansions", 60, THREAD, "user"), Job("scheduled posts", 30, CREATE, "user"), Job("media uploads", 20, READ, "user"), Job("signup backfill", 12, COMPLETE, "config"), Job("account connect", 1, LOGIN, "user"), ] def seat_cogs(jobs=SEAT) -> float: return sum(j.monthly_usd for j in jobs) def gross_margin(seat_price: float, jobs=SEAT) -> float: return (seat_price - seat_cogs(jobs)) / seat_price def unbounded_share(jobs=SEAT) -> float: """Fraction of cost the CUSTOMER controls. Above ~0.3, a flat seat price is a bet rather than a price.""" total = seat_cogs(jobs) user = sum(j.monthly_usd for j in jobs if j.bounded_by == "user") return user / total if total else 0.0 ``` The `bounded_by` field is the part worth copying even if you throw away the rest. Splitting the call graph into the half you control and the half the customer controls is the single distinction that decides your plan structure, and putting it in the data model means nobody can add a job without answering the question. Once the model exists, a test keeps it honest. This is the assertion that would have caught every unlimited-plan failure described earlier, and it costs four lines. ```python def test_cheapest_plan_is_not_underwater(): """The margin floor is a product decision. Encode it, or the next person to tighten a poll interval will move it without knowing.""" assert gross_margin(seat_price=9.00) > 0.60, ( f"cheapest plan margin {gross_margin(9.00):.2%} below floor; " f"seat COGS is now ${seat_cogs():.4f}" ) def test_customer_controlled_share_is_bounded(): assert unbounded_share() < 0.30, ( "more than 30% of seat cost is user-driven, so the flat price " "is exposed to a heavy account. Meter it or cap it." ) ``` A failing test in CI when somebody drops the poll interval from fifteen minutes to five is worth more than any amount of documentation about why the interval matters. It moves the cost question from a thing people are supposed to remember to a thing the build refuses to skip. The last piece is the ceiling, and it belongs in the request path rather than in a monitoring job. A ceiling that alerts is a record of an overrun. A ceiling that refuses is a prevention of one. ```python class BudgetExceeded(Exception): """Raised before the call, never after. Carries the numbers the support conversation will need.""" def guarded_call(endpoint, account_id, month_budget_usd, **params): spent = usage_log.month_to_date_usd(account_id) rate = RATES.get(endpoint, 0.0008) if spent + rate > month_budget_usd: raise BudgetExceeded( f"account {account_id} at ${spent:.4f} of " f"${month_budget_usd:.2f} this month; {endpoint} would " f"add ${rate:.4f}. Raise the plan limit to continue." ) return call(endpoint, account_id, **params) ``` Two things about that function are deliberate. It checks before the call rather than after, so the budget is a limit rather than a report. And the exception carries the actual figures, so when a customer asks why something stopped, the answer is a sentence rather than an investigation. ## What changes as you scale The model above is written for a per-seat product because that is the most common shape. Three variants come up often enough to be worth naming, because each one moves a different term. **Per-account rather than per-seat.** If your customers are agencies managing many client accounts, the unit of cost is the managed account and the unit of revenue is the seat, and those are different denominators. An agency with one seat and forty client accounts is forty times the cost of a solo user on the same plan. Model per managed account, price per managed account, and let seats be free. Products that get this backwards discover it when their first agency customer arrives, which is usually also their largest customer. **Shared rather than per-customer reads.** If ten customers all track the same popular account, you should be making one call and fanning the result out, not ten calls. That collapses the cost of the overlapping portion of every watchlist and it is the single largest structural saving available in this class of product. It also changes the model: the marginal cost of the eleventh customer tracking that account is zero, which finally gives you back some of the software economics. The cost becomes a function of the union of all watchlists rather than the sum of them, and on a mature product that union is far smaller than the sum. **Burst rather than steady.** Some products are quiet most of the month and enormous for two days, around a launch, an earnings call or an event. A monthly average cost is meaningless for these, and a monthly budget check will happily allow a single day that consumes everything. Model the peak day, not the mean, and put the ceiling on a window short enough to catch it. Our [real-time tracking guide](/blogs/track-tweet-performance-real-time-x-api) covers the design side of burst workloads, and the [monitoring coverage honesty post](/blogs/twitter-monitoring-api-coverage-honesty-2026) covers what a poller does when it cannot keep up, which is the failure mode that hides inside a burst. There is a fourth variant that is less a scaling question than a strategic one, and it belongs here because it is the decision the whole model is meant to inform. At some volume, for some workloads, the correct answer stops being a per-call API and becomes a bulk arrangement, a cached corpus, or building the collection yourself. The model tells you where that line is for your workload, which is the only way to have that conversation without it turning into a debate about vendor loyalty. If the arithmetic says a per-call rate costs more than the alternative at your volume, that is a finding rather than an insult, and a vendor worth using will tell you the same thing. For the workload-level version of this arithmetic, where the question is what a specific pipeline costs rather than what a specific seat costs, our [companion analysis of cost by workload](/blogs/twitter-api-cost-by-workload-2026) runs the same method across sentiment analysis, bot detection and lead scoring. It is the other half of this post: this one prices a user, that one prices a job. Further reading on the general problem of metered dependencies inside a subscription business: the [Anthropic pricing page](https://www.anthropic.com/pricing) is a clean example of a vendor publishing a rate card that a customer is expected to model against, and the [BigQuery pricing documentation](https://cloud.google.com/bigquery/pricing) is the canonical example of a service where the cost is entirely a function of how you write your queries rather than of which plan you bought. Both are worth reading as examples of the genre before you write your own. ## Frequently Asked Questions ### What does X API cost per user actually mean? It means the variable data cost your product incurs to serve one account for one billing period, and it is a cost of goods sold line rather than an infrastructure line. You get it by writing down the call graph one seat triggers in a month, multiplying each call type by its billed rate, and summing. It is not the per-call price on a vendor page, and it is not your total monthly bill divided by headcount, because that average hides the power users who actually decide your margin. The number moves with poll interval, backfill depth and how many accounts each seat tracks, so it is a design output rather than a fixed property of the API you chose. ### How do I convert a per-call rate into a monthly per-seat figure? List every scheduled job and every user action that touches the API, give each one a frequency per seat per month, and multiply. A fifteen minute poll is ninety six calls a day and two thousand eight hundred and eighty a month, so at a standard read rate of $0.0008 per call it costs $2.30 per seat per month before anything else happens. A five minute poll on the same endpoint costs $6.91. Everything else on a typical seat, the daily competitor sweep, the thread expansions, the scheduled posts, usually sums to well under a dollar. The poll interval is almost always the term that dominates. ### Why is a per 1,000 tweets figure misleading? Because you are billed per call and not per tweet, so the per thousand figure depends entirely on how many tweets each call returns. Our own billing logs across 396,817 successful tweet-returning read calls between 2026-08-13 and 2026-08-17 show timeline reads averaging 18.78 tweets per call and search reads averaging 7.62, with 29.5 percent of those search calls returning nothing at all. The same $0.0008 buys $0.043 per thousand on one path and $0.105 per thousand on the other. Quote the per call rate as the price and treat any per thousand figure as a model with its assumption stated beside it. ### At what seat price does an X data feature stop being profitable? It depends on the call graph, not the API. Modelling a heavy seat that carries a five minute mentions poll, a five account competitor sweep, sixty thread expansions, thirty scheduled posts, a signup backfill and one account connection gives a monthly cost of $10.16. On a $9 seat that is a gross margin of negative 12.88 percent. The same seat at $29 returns 64.97 percent and at $49 returns 79.27 percent. If you want to hold eighty percent gross margin on a $29 seat, your entire data budget is $5.80, which is 7,250 standard calls a month, or one call every 5.96 minutes if a single poller is all you run. ### Should I charge per seat or per credit for a data-backed feature? Charge per seat when the call graph per seat is bounded by something you control, such as a fixed poll interval and a capped watchlist. Charge per credit when the user decides the volume, such as exports, backfills and bulk enrichment, because those are the actions that turn one customer into a hundred customers worth of cost. Most products that survive end up hybrid: a seat price that covers a modelled baseline, plus metered credits for the unbounded actions, plus a hard ceiling that fails loudly rather than silently spending. The hybrid is not a pricing compromise, it matches the two different cost shapes the product actually has. ### How do I attribute API spend to a specific user? Log a row per outbound call with the account id, the endpoint, the billed tier and a timestamp, and do it at the HTTP client rather than at the feature. Attribution added at the feature layer misses retries, background jobs and anything a shared helper calls, which is usually where the cost actually is. With a per call log you can compute cost per account for any window with one aggregate query, rank accounts by spend, and see the distribution rather than the mean. Without it you have a monthly invoice and a guess, and the guess is always wrong in the same direction. ### Does caching actually reduce X API cost per user? It reduces it for the reads that repeat, which on most products is a large share. Profile lookups, follower counts and tweet detail on already-seen posts are all safe to cache for minutes or hours depending on how fresh the feature needs to be. What caching cannot help is the poll itself, because the whole purpose of the poll is to discover something new, and a cache hit there means you did not look. Model the poll separately from the lookups, cache the lookups aggressively, and spend your remaining budget on poll frequency where it changes the product. ### What does the free signup credit realistically cover? A $0.50 signup credit divided by the $0.0008 standard rate is 625 standard calls. What that means in tweets depends on the path you take. At the measured timeline yield of 18.78 tweets per call it lands at 11,738 tweets, at the measured search yield of 7.62 it is about 4,762, and at a modelled full twenty tweet page it would be 12,500. For a prototype that is enough to build and test the call graph end to end. It is not enough to run a production poll, since a single fifteen minute poller consumes the entire credit in roughly six and a half days. ### Why does the poll interval dominate a per-seat cost model? Because a poll is the only line in the call graph whose frequency is not bounded by human behaviour. A user reads a limited number of threads a month because there are only so many hours in a day, but a poll runs on a clock and never tires. A fifteen minute poll is 2,880 calls a month and a five minute poll is 8,640, so on the standard read rate they cost $2.30 and $6.91 per seat while every other job on a typical seat together comes to well under a dollar. Any term multiplied by several thousand stops being small, which is why the interval and not the endpoint choice is the number to argue about. ### What should I do if my data cost is genuinely tiny today? Confirm it in writing and then stop, which takes an afternoon. The test is not whether the cost is small now but whether the mechanism generating it is bounded. A small cost produced by an uncapped pagination loop, an unlimited watchlist or a retry policy with no ceiling is a large cost that has not happened yet, and the difference is invisible in a monthly total. Write the call graph down, note which lines are bounded by your own configuration and which are bounded by customer behaviour, and if every line is in the first group you are genuinely finished until the product changes.