Official Python SDK for the Chocodata web scraping API.
Typed methods for the endpoints most people start with, a generic client for the rest of the catalog, retry with backoff, and a structured error type. No dependencies: standard library only.
pip install chocodataPython 3.9+.
from chocodata import Chocodata
chocodata = Chocodata("asa_live_YOUR_KEY")
product = chocodata.amazon.product(query="0143127748")
print(product["title"], product["price"])Get a key at chocodata.com. The free tier is 1,000 requests, one time, no card.
The key travels as a query parameter, which the SDK handles for you. There is no header auth: sending Authorization: Bearer or X-API-Key returns 401.
Every method below was smoke-tested against production before release.
# Ecommerce
chocodata.amazon.product(query="0143127748") # ASIN or ISBN
chocodata.amazon.product(query="0143127748", domain="de") # prices localise to the marketplace
chocodata.amazon.search(query="laptop")
chocodata.walmart.product(url="https://www.walmart.com/ip/19075520026")
chocodata.walmart.search(query="laptop")
chocodata.ebay.product(url="https://www.ebay.com/itm/298520538688")
chocodata.ebay.search(query="laptop")
# Search engines (note: Bing takes `q`, not `query`)
chocodata.bing.search(q="coffee", count=10)
chocodata.bing.images(q="red panda", count=20, safe_search="strict")
# Social / video
chocodata.tiktok.profile(username="rotana")
chocodata.youtube.video(video_id="dQw4w9WgXcQ")
chocodata.instagram.profile(username="nasa")Parameters are not uniform across targets, and the SDK does not pretend otherwise. amazon.product takes query; walmart.product takes url or id and rejects query; bing.search takes q.
The catalog has 499 endpoints. Anything without a dedicated method is reachable through the same code path:
chocodata.scrape("bing", "videos", {"q": "coffee"})
chocodata.scrape("reddit", "post", {"post_id": "627akk", "subreddit": "askscience"})from chocodata import Chocodata, ChocodataError
try:
chocodata.tiktok.profile(username="a-handle-that-is-gone")
except ChocodataError as e:
print(e.status) # 404
print(e.code) # 'item_not_found'
print(e.retryable) # False
print(e.request_id) # for support| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_params |
A required parameter is missing or the wrong type. The body names it. |
| 401 | INVALID_API_KEY |
Key missing, unrecognised, or revoked. |
| 402 | INSUFFICIENT_CREDITS |
Balance exhausted. |
| 404 | item_not_found |
The item genuinely does not exist. Not retryable. |
| 429 | RATE_LIMITED |
Over 120 requests / 60s, or over your plan's concurrency. |
| 502 | extraction_failed / target_unreachable |
Retryable; the SDK already retried. |
You are only charged on a 2xx. Errors cost no credits.
Retryable failures (429, 5xx, network) are retried with exponential backoff plus jitter. The SDK trusts the server's retryable flag, so a 404 is never retried.
chocodata = Chocodata(
"asa_live_YOUR_KEY",
max_retries=3, # default 2
timeout=120.0, # default 90.0
debug=True, # log every call
)AsyncChocodata runs each request in a worker thread via asyncio.to_thread, so it composes with asyncio.gather. It is a concurrency wrapper, not a native async HTTP stack.
import asyncio
from chocodata import AsyncChocodata
async def main():
client = AsyncChocodata("asa_live_YOUR_KEY")
asins = ["0143127748", "B09B8V1LZ3", "B0BSHF7WHW"]
results = await asyncio.gather(*[
client.ascrape("amazon", "product", {"query": a}) for a in asins
])
for r in results:
print(r["asin"], r.get("title"))
asyncio.run(main())Requests are limited to 120 per key per 60 seconds, plus a per-plan concurrency ceiling.
MIT