Answers

Can a pay-per-call data API change its prices in the middle of my project?

Short answer

Yes. Any vendor can change prices, and several have, X among them. On okdata each call is charged the endpoint's price at the moment of the call (X post search is $0.006 today, Douyin search $0.02), the price book has a version number your script can watch, and every response states its exact charge, so a change shows up on the first call after it.

Price changes in data APIs are common enough to plan for. X replaced its $200-a-month Basic plan with pay-per-use credits in 2026. Users of a popular X scraper on a marketplace questioned its new pricing per 1,000 results. On V2EX, developers noticed API resellers raising prices across the board. None of this is unusual. What matters is whether you notice the same day, or at month end.

What okdata gives you to notice

  • A versioned price book. https://okdata.online/prices.json lists every endpoint's price and carries a price_book version. If it changes, something was repriced.
  • The charge on every response. meta.billing.amount is the exact cost of that call: X post search at $0.006, Douyin video search at $0.02.
  • No commitment to unwind. With a prepaid balance and no plan, you can stop or switch the day prices stop working for you.

There is no price lock for a fixed period. For a long contract-style project, ask hello@okdata.online in writing.

Make your pipeline notice

import json, os, pathlib, requests
from decimal import Decimal

book = requests.get("https://okdata.online/prices.json", timeout=30).json()
seen = pathlib.Path("price_book.json")
if seen.exists() and json.loads(seen.read_text())["price_book"] != book["price_book"]:
    print("Price book changed: review prices before this run")
seen.write_text(json.dumps({"price_book": book["price_book"]}))

expected = {e["id"]: Decimal(e["price"]) for e in book["endpoints"]}
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
body = requests.get("https://api.okdata.online/v1/x/posts/search", headers=H,
                    params={"query": "your brand", "sort": "latest"}, timeout=60).json()
charged = Decimal(body["meta"]["billing"]["amount"])
if charged > expected["x.posts.search"]:
    raise SystemExit(f"Charged {charged}, expected {expected['x.posts.search']}: stopping")

Protect the budget itself

  • Prefer short commitments. Annual prepayment buys a discount but makes a reprice painful. Pay-per-call with a modest balance limits exposure to what is already loaded.
  • Keep the vendor behind one function. If your code calls search_posts() and not a vendor's URL directly, switching costs an afternoon instead of a rewrite.
  • Budget with headroom. Price the plan at today's rates, then add 20 to 30 percent for re-runs and repricing.
  • Re-estimate before big pulls. A two-minute check against the price book before a 50,000-call backfill is cheap insurance.

Current prices and the rules for what is never charged are on the pricing page.

Updated