SDKs
Thin, typed clients for JavaScript/TypeScript and Python — built-in retries, typed errors, one-line page fetches.
Install
The SDKs live in the repository under packages/ and are not published yet.
cd packages/sdk-js && npm install && npm run build
npm install ./packages/sdk-js # in your projectpip install ./packages/sdk-python # requires httpxJavaScript / TypeScript
import { Teneo } from "@teneo/contents";
const client = new Teneo({ apiKey: process.env.LIVE_WEB_DATA_API_KEY, baseUrl: "https://livewebdata.com/api" });
const page = await client.getPage("https://example.com/article", { country: "de", format: "markdown" });
console.log(page.title, page.credits);
const res = await client.contents({ urls: ["https://example.com/a", "https://example.com/b"] });
const account = await client.account();Python
import os
from teneo import Teneo
client = Teneo(api_key=os.environ["LIVE_WEB_DATA_API_KEY"], base_url="https://livewebdata.com/api")
page = client.get_page("https://example.com/article", country="de", format="markdown")
print(page.title, page.credits)
res = client.contents(urls=["https://example.com/a", "https://example.com/b"])
account = client.account()Both clients read the key from LIVE_WEB_DATA_API_KEY and the base URL from LIVE_WEB_DATA_API_BASE_URL when you don't pass them.
Methods
| JS | Python | Description |
|---|---|---|
contents(request, { idempotencyKey }) | contents(urls=…, country_code=…, text=…, html=…, livecrawl_timeout=…, render=…, render_fallback=…, idempotency_key=…) | Raw POST /v1/contents (1–10 URLs). render_fallback=False / renderFallback: false turns the default browser fallback off |
getPage(url, opts) | get_page(url, country=…, format=…) | One URL → { title, url, content, credits, source }; raises if that URL failed. format: "html" requests the HTML (html: true) |
account() | account() | GET /v1/account |
Errors
| Error | When |
|---|---|
TeneoAuthError | 401 / 403 — key invalid, revoked or suspended |
TeneoInsufficientCreditsError | 402 |
TeneoRateLimitError | 429 after all retries (retryAfter / retry_after) |
TeneoIdempotencyError | 409 — the idempotency key was used with a different request, or its result expired (15 minutes) |
TeneoRestrictedTargetError | 451 |
TeneoConnectionError | Network error or timeout after all retries, or a 2xx answer cut while it was read. The API may have received the request: repeat it with the error's idempotencyKey / idempotency_key |
TeneoPendingError | Still 202 when maxWaitMs / max_wait ran out; idempotencyKey / idempotency_key collects the result later |
TeneoPageError | getPage / get_page: the URL failed (tag, e.g. CRAWL_NOT_FOUND) |
TeneoAPIError | Base class (status, tag, requestId, and idempotencyKey / idempotency_key: the key of a contents() call, else null / None) |
Retries
Both SDKs retry 429 (respecting Retry-After), 5xx, network errors, a 2xx answer cut while it is read and — for getPage — CRAWL_LIVECRAWL_TIMEOUT, up to 3 times with exponential backoff and jitter. 400, 401, 402, 403, 409 and 451 are never retried. Configure with maxRetries / max_retries.
Every attempt of one contents() call sends the same Idempotency-Key, so a retry picks up the first fetch and is never charged twice (render requests included). A fetch that runs longer than about 100 seconds answers 202; the SDKs repeat it after Retry-After until the result arrives, for up to 5 minutes (maxWaitMs / max_wait). These repeats do not count as retries. See Long requests.
Without your own key, the SDKs create one per contents() call. When the retries run out, the error carries that key: call contents() again with the same request and { idempotencyKey: e.idempotencyKey } / idempotency_key=e.idempotency_key within 15 minutes, and a fetch the API already received returns its result instead of being charged again. Better: always pass your own key (one per logical request, for example per row of your input), so a request cut by a network error, a timeout or a deploy can be retried without a second charge even after your process restarts.