X-Autopilot

X API compliance: the deletion job you owe X

Batch compliance jobs, the 24-hour deletion rule and the Enterprise-only event streams: what X API compliance requires in 2026, and what obeying it costs.

X-Autopilot Team··11 min read
On this page · 9 sections

The short version

  • ▸If you store X content offline, X's Developer Policy requires you to delete or modify it to match the current state on X, as soon as reasonably possible and within 24 hours of a request from X or the account owner. That obligation exists whether or not you ever call a compliance endpoint.
  • ▸There are three ways to find out what changed, and only one is open to a card-funded account: Batch Compliance jobs. The near real-time Post and User compliance event streams are Enterprise access only.
  • ▸Checking is unpriced. Obeying is not. A 'delete' costs you nothing, but every 'rehydrate' means re-fetching that Post, billed as a Post read at $0.005 each.
  • ▸A job carries five hard limits: one active job per type, a 100 MB upload (about 5 million IDs), a 15-minute upload window, a 7-day download window, and a 36-hour processing ceiling after which the job fails.
  • ▸The upload and download routes were broken on pay-per-use from late August 2026 until 21 September 2026, confirmed by X staff on 8 October. They now require an Authorization: Bearer header that the old Google Cloud Storage URLs did not.

Quick answer

X API compliance is not an endpoint you choose to call. It is a clause in the Developer Policy: if you store X content offline, you have to keep it matching the current state on X, and act within 24 hours of a request from X or the account owner. The Batch Compliance endpoints are how you find out what changed, and on a pay-per-use account they are the only way. The near real-time compliance event streams are Enterprise.

Last updated: October 2026

That framing matters because the obligation runs the other way from everything else on the X API. Every other endpoint is something you want. This one is something you owe, and it is the only part of the platform where doing nothing is a breach rather than a saving.

What the policy actually says

Two documents, one rule.

X's Developer Policy has a section titled Content compliance. It opens with a sentence in bold: if you store X content offline, you must keep it up to date with the current state of that content on X. You must delete or modify anything you hold if it is deleted or modified on X, "as soon as reasonably possible, or within 24 hours after receiving a request to do so by X or the applicable X account owner."

Then it defines modified, and this is the part people miss. Modified content is not just an edited Post. It includes content made private or given protected status, content suspended from the platform, content that has had geotags removed, and content withheld or removed from X.

The Developer Agreement, last updated 27 April 2026, repeats the window in Section II.B under the heading Removals, with the same 24-hour clock running from a written request. Section I goes further: on termination you must permanently delete all Licensed Material in every form, and on request produce evidence of deletion, such as screenshots, within ten business days.

So the obligation covers six kinds of change, has a hard clock, and survives the end of your API access. None of that is unusual for a data licence. What is unusual is the gap between the obligation and the tooling.

Three ways to find out, one you can actually buy

SurfacePathAccessShape
Batch Compliance/2/compliance/jobsPay-per-use, EnterpriseAsync job, you upload IDs
Post compliance streamCompliance event streamEnterpriseNear real-time push
User compliance streamCompliance event streamEnterpriseNear real-time push
Activity API webhookspost.delete and friendsPay-per-use, EnterprisePush, subscribed accounts only

The two compliance event streams are the ones you want and cannot have. X documents them properly: the Post stream emits delete, tweet_edit, withheld, drop and undrop; the User stream emits eight events including user_protect, user_suspend, user_unsuspend and user_profile_modification. The page states plainly that both are available with Enterprise access, for partners ingesting high volumes.

That leaves Batch Compliance, which is a polling tool wearing a job queue. You hand X a file of IDs and it hands back what changed. The design assumption is that you already know every ID you hold, which is true for a database and false for a cache.

There is a fourth option worth knowing if your data comes from accounts that authorised your app rather than from the public timeline. The X Activity API pushes account-level events to a webhook, and in X's webhook event table post.delete is priced as "Not billed". A real-time, no-cost delete signal exists. It just only covers accounts subscribed to your app.

How a job runs, and the five limits inside it

Four calls, in order. POST /2/compliance/jobs with {"type": "tweets"} or {"type": "users"}, which returns an upload_url and a download_url. PUT your IDs to the upload URL as plain text, one numeric ID per line. Poll GET /2/compliance/jobs/:id until the status is complete. GET the download URL for newline-delimited JSON.

Five limits decide whether your build survives:

  • One active job per type. A second tweets job while one is created or in_progress returns 409. A users job can run alongside a tweets job, so your effective parallelism is two.
  • 100 MB per upload, roughly 5 million IDs. Over that is a 413. Gzip is accepted with Content-Encoding: gzip and ID lists compress about 4x, so a maximum submission travels as about 25 MB, but the 100 MB ceiling applies to the decompressed content.
  • A 15-minute upload window. The URL is not refreshable. Miss it and you cancel the job and create another.
  • A 7-day download window. Results are not retrievable afterwards, so persist them on receipt rather than reading them live.
  • A 36-hour processing ceiling. Past that the job ends failed. Processing time tracks the time span your IDs cover, not the file size, which is why X recommends partitioning by era rather than by volume: one job for IDs older than a year, another for the rest.

Two quiet contract details are easy to get wrong. Absence means clean, so an ID missing from the results needs no action. And you get one event per ID, the highest-impact one, with delete outranking rehydrate outranking scrub_geo.

Checking is unpriced. Obeying is billed.

Search X's pay-per-use rate card for the word compliance and you get nothing. There is no per-ID charge, no per-job charge, no compliance row between List: Read and Space: Read. The three job endpoints appear on the rate limits page instead, at 150 requests per 15 minutes per app with no user-level limit.

All of it sits one step downstream, in the action field.

delete is free. You remove rows from your own database and X never hears about it. scrub_geo is free for the same reason: you null a column.

rehydrate is the expensive one. It means the Post was edited and your copy is stale, and the only fix is to re-fetch it through a Posts lookup, billed as a Post read at $0.005. Run a 5-million-ID archive through a job, get 1% back as rehydrate, and that is 50,000 Post reads, or $250 at list price. The request count is trivial, since GET /2/tweets takes 100 IDs at a time and sits well inside its app limit. The credits are not. Note also that those reads land against the same 3 million Post read monthly cap as the rest of your pipeline, so a large archive sweep competes with your product.

There is a cheaper read on this: rehydrate only arrives for Posts that were edited, and editing requires Premium and a 60-minute window. If your corpus skews to accounts that do not pay for X, your real rehydrate rate will be far under 1%. Measure it on a sample job before you budget the sweep.

The two months nobody wrote about

In late August 2026, X moved the compliance upload and download URLs off Google Cloud Storage signed URLs and onto token-bearing paths on api.x.com. The control plane kept working. The data plane did not.

The timeline, assembled from four separate forum threads:

  • 25 August 2026. The last job to upload successfully on one affected app used a storage.googleapis.com URL, while that app was still on Pro.
  • 26 August. The same app moved to pay-as-you-go. Its upload_url came back on api.x.com, and the PUT returned a bare 404 with an empty body.
  • 27 August. A second developer reported the identical pattern on a pay-per-use project: create, list and get all returning 200, both signed URLs 404 in about 1 millisecond with content-length: 0. X staff called it "not expected" and opened an enrollment check.
  • 31 August. A third app, same signature, right down to the 4-millisecond response time.
  • 10 September. The second developer created a brand-new app in the same project and reproduced the failure in the same minute, which ruled out per-app enrollment.
  • 21 September. The routes were restored, though nobody was told at the time.
  • 8 October. X staff confirmed the cause: the upload and download endpoints "were still being restored" when the August tests ran, and were fully restored on 21 September. Nothing about the apps, request shapes or tokens had been wrong.

One change survived the fix and it is the thing to copy into your client: the upload PUT and the download GET now need your Authorization: Bearer header. The old signed URLs did not. If you are porting an integration written before September 2026, that header is the migration.

Through all of it, the 24-hour clause in the Developer Policy never paused. For roughly four weeks, the only compliance tool available to a self-serve account did not work, and one developer said plainly that batch compliance round-tripping was a launch gate they would not ship without.

Three things the docs still get wrong

The resumable flag that does nothing. The OpenAPI schema for POST /2/compliance/jobs still exposes a resumable boolean described as enabling "support for resumable uploads", while the integration guide says flatly that there is no resumable or chunked mode. In September 2026 sending resumable: true returned a 400 telling the caller to create the job without it. By October it was accepted and simply ignored, with the created job coming back "resumable": false. Read the flow off the job object, never off your request.

The cancel endpoint that is not in the index. On 1 October 2026, with a job stuck in_progress past 36 hours and every subsequent job 409ing, X staff wrote that there was no public cancel or DELETE for compliance jobs. There is one now: DELETE /2/compliance/jobs/{id}, documented at API version 2.172, which fails the job and frees the type slot. It appears in neither the rate-limit table, which still says "Compliance (3 endpoints)", nor the documentation index. If you hit a wedged queue, the fix exists and the map does not show it.

A rate limit that cannot be reached. POST /2/compliance/jobs is limited to 150 requests per 15 minutes. One job per type can be active. The second rule makes the first unreachable, which is a good hint about where the real constraints live: in the job lifecycle, not the request budget.

A compliance loop that survives

What actually works, in order:

  1. Keep an ID ledger. Every stored Post ID and user ID, with the date you first stored it. You cannot submit what you have not tracked, and this is the single most common reason a compliance story falls apart under audit.
  2. Partition by era, not by size. One job per time bucket. It keeps you under the 36-hour ceiling and makes a failure cheap to retry.
  3. Upload inside the same code path that creates the job. Fifteen minutes is generous for a script and useless for a human workflow with an approval step in it.
  4. Store the job ID with the submission. GET /2/compliance/jobs lists recent jobs, but correlating a job back to the ID set you sent is your problem, not X's.
  5. Download on completion and keep the raw results. Seven days, then the results are gone. The raw file is also the only evidence you have that you acted.
  6. Treat a 409 as a stuck-job alarm, not a retry. Check the active job's age, and cancel past 36 hours.
  7. Log what you deleted and when. The Developer Agreement contemplates X asking for evidence of deletion. A dated log is the cheap version of that.

Daily or weekly is the cadence X describes as the intended pattern. Pick one, schedule it, and alert on the job not finishing rather than on the job failing.

If you are not storing X content

Most people who end up on a page about X API compliance do not have a data warehouse. They want to post, reply and grow an account, and the API is one route to that.

Worth being clear: if you never store X content offline, the Content compliance clause has nothing to attach to. A scheduler that holds your own drafts and reads your own metrics is not a corpus. The obligation arrives the moment you keep other people's Posts, and it scales with how many you keep, which is a real argument for storing IDs plus your own derived fields instead of full Post bodies. IDs still need compliance checks, but a rehydrate on a Post you never cached costs you nothing to honour.

If the goal is consistent posting and replying rather than a data pipeline, the API is not the only route and it is not always the cheapest one. X-Autopilot drives your own browser on your own machine, so there is no stored corpus of other people's Posts to keep in sync, and no metered read per Post you look at. It is a different set of trade-offs, and we are direct about them: browser automation is a grey area under X's automation rules, while the API route is the sanctioned one. Neither removes risk from your account.

Either way, the rule underneath is the same one X has been consistent about for years. When somebody deletes a Post or locks their account, that decision is supposed to reach every copy. The endpoints are how you find out. The clock is not optional. For the user-facing side of the same mechanic, see how to delete a post on X and how to make your X account private.

Frequently asked

Answers indexed by Google + AI assistants.

What is X API compliance?+

It is the requirement in X's Developer Policy that any X content you store offline stays in sync with the current state of that content on X. If a Post is deleted, edited, geo-scrubbed, withheld, or its author goes private, is deactivated or is suspended, your stored copy has to change too. The Batch Compliance endpoints and the compliance event streams are the tools X provides to find out what changed.

How long do I have to delete a Post after it is deleted on X?+

X's Developer Policy says 'as soon as reasonably possible, or within 24 hours after receiving a request to do so by X or the applicable X account owner.' The Developer Agreement, last updated 27 April 2026, uses the same 24-hour window in its Removals section and extends it to content that gains protected status or is suspended, withheld, modified or has location data removed.

How much does a batch compliance job cost?+

X's pay-per-use pricing page has no compliance line at all, and the three job endpoints are rate limited rather than metered, at 150 requests per 15 minutes each. The cost sits downstream: acting on a 'rehydrate' event means re-fetching the Post through a lookup endpoint, which is billed as a Post read at $0.005 per Post.

Can I get compliance events in real time?+

Not on a self-serve account. X documents two near real-time compliance event streams, one for Posts and one for users, and both are Enterprise access. The nearest self-serve substitute is the X Activity API, whose webhook event list prices post.delete as 'Not billed' - but that only covers accounts subscribed to your app, not arbitrary public Posts you have stored.

Why does my compliance upload_url return 404?+

If you saw this between late August and 21 September 2026, it was X's own data-plane outage during a migration off Google Cloud Storage signed URLs, acknowledged by X staff on 8 October 2026. After the fix, the upload PUT and the download GET both require your Authorization: Bearer header, which the legacy signed URLs did not. A missing Bearer on those two calls is now the most likely cause of a 401 or 403.

What do I do when a compliance job gets stuck?+

Only one job per type can be active, so a stuck job blocks every job behind it with a 409. X staff said on 1 October 2026 that no public cancel existed. A DELETE /2/compliance/jobs/:id endpoint is now documented, which fails the job and frees the slot, though it is still missing from X's own rate-limit table and documentation index.

Sources
Related searches
x api compliancex api batch compliancebatch compliance job api/2/compliance/jobsx api compliance streamtwitter api delete stored tweets requirementx developer policy content compliancex api rehydrate actionx api pricingx api rate limitsx developer agreementtwitter api data retentionx api v2 endpointspost deleted detectionpay per use api credits
DY
Deepak YadavBuilding X-Autopilot

Product designer and indie hacker. Runs the agent on his own X account every day and writes up what the data shows, including when it's inconvenient.

Follow on X →
Try X-Autopilot.
$199 once. No subscription, no monthly bill. Real Chrome on your Mac.
See pricing▶
Free PDF · the X Growth Playbook

The exact playbook we use to grow on X

The bio that converts, the daily reply loop, the posting cadence, and the tool stack, in one no-fluff PDF. Drop your email and it's yours.

No spam. Unsubscribe anytime.

Free tools

Try it yourself.

All free X tools →
Keep reading

Related posts.