How to Scrape Audible Product Pages
Learn how to retrieve authorized Audible product metadata, parse JSON-LD safely, handle locales and failures, and choose an API workflow.

Scraping an Audible product page is technically possible: you can retrieve a known /pd/ URL and parse structured metadata such as title, author, narrator, rating, price, currency, and availability. The difficult part is authorization. Audible’s License Agreement says the license does not include commercial use, collection or use of product listings, descriptions, or prices, or data mining, robots, and similar extraction tools. Its Conditions of Use prohibit systematic extraction and reuse without express written permission.
For a production project, obtain written permission or a licensed feed before collecting data. Define the permitted domains, locales, fields, request rate, storage period, redistribution rights, and a contact for deletion or change requests. The examples below show an authorized, small-scale workflow. They do not bypass CAPTCHAs, login controls, robots rules, paywalls, DRM, or other technical barriers.
What Audible product pages can contain
A normalized audiobook record commonly needs more than a title and price. The official ACX Book Posting Agreement identifies metadata categories such as a digital ISBN or similar identifier, title, genre, author, publisher, initial publication date, territory, and list price. Community documentation and technical guides also describe fields commonly exposed in product-page markup:

| Field | Typical source | Important handling detail |
|---|---|---|
| Title | JSON-LD name or visible heading |
Keep the original locale and punctuation. |
| Author | Contributor links or visible labels | There may be multiple authors. |
| Narrator | Contributor section | Store an array; editions can have multiple narrators. |
| ASIN or identifier | URL, page metadata, or product details | Validate before using it as a key. |
| Price | JSON-LD price or visible purchase panel |
Store the value as a locale-specific string plus currency. |
| Currency | JSON-LD priceCurrency |
Never infer currency from a symbol alone. |
| Availability | JSON-LD availability |
Availability changes and can be marketplace-specific. |
| Rating and review count | Aggregate-rating markup or visible text | Treat both as time-sensitive snapshots. |
| Genre, publisher, date, territory | Product details or licensed metadata | Record the marketplace and retrieval time. |
Markup is volatile. Fields can disappear or vary with locale, experiments, login state, availability, or marketplace. Preserve the raw response and retrieval timestamp beside normalized values so a later parser change can be audited.
Authorization checklist before the first request
- Get written authorization. The document should identify the Audible domains and locales covered, the exact fields, maximum request rate, storage duration, redistribution rights, and an owner for takedown requests.
- Start with known URLs. Use a small set of approved
/pd/URLs. Do not crawl search results or build a catalog unless the permission explicitly covers those activities. - Define a stop policy. Stop on a CAPTCHA, access-denied response, login wall, terms change, or any instruction from the rights holder.
- Protect account data. Do not collect credentials, personal data, cookies belonging to another user, or DRM-protected content.
- Document retention. Record when each page was fetched, which authorization applied, and how to delete or update the record.
Direct retrieval with Python
The following script retrieves one approved product URL, extracts JSON-LD first, and falls back to a few visible labels. It deliberately uses a normal HTTP request and a conservative timeout. Install dependencies with python -m pip install requests beautifulsoup4.
import json
import re
from datetime import datetime, timezone
from urllib.parse import urlparse
import requests
from bs4 import BeautifulSoup
URL = "https://www.audible.com/pd/APPROVED-PRODUCT-URL"
headers = {
"User-Agent": "AuthorizedMetadataCollector/1.0 (contact: data-owner@example.com)",
"Accept-Language": "en-US,en;q=0.8",
}
response = requests.get(URL, headers=headers, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
json_ld = []
for node in soup.select('script[type="application/ld+json"]'):
try:
value = json.loads(node.string or node.get_text())
json_ld.extend(value if isinstance(value, list) else [value])
except json.JSONDecodeError:
continue
product = next((x for x in json_ld if isinstance(x, dict) and (x.get("@type") == "Product" or "name" in x)), {})
record = {
"url": URL,
"retrieved_at": datetime.now(timezone.utc).isoformat(),
"marketplace": urlparse(URL).netloc,
"title": product.get("name"),
"price": (product.get("offers") or {}).get("price") if isinstance(product.get("offers"), dict) else None,
"currency": (product.get("offers") or {}).get("priceCurrency") if isinstance(product.get("offers"), dict) else None,
"availability": (product.get("offers") or {}).get("availability") if isinstance(product.get("offers"), dict) else None,
"rating": (product.get("aggregateRating") or {}).get("ratingValue") if isinstance(product.get("aggregateRating"), dict) else None,
"review_count": (product.get("aggregateRating") or {}).get("reviewCount") if isinstance(product.get("aggregateRating"), dict) else None,
}
# Small, permission-scoped fallback for labels that are not in JSON-LD.
def labeled_value(label):
text = soup.get_text(" ", strip=True)
match = re.search(rf"{re.escape(label)}\\s*[:\\-]?\\s*([^|]+)", text, re.I)
return match.group(1).strip() if match else None
record["author_text"] = labeled_value("Written by")
record["narrator_text"] = labeled_value("Narrated by")
print(json.dumps(record, indent=2, ensure_ascii=False))
Run this only against URLs covered by your authorization. A successful HTTP response does not mean every field is present or that collection is permitted.
Equivalent requests with cURL and Node.js
cURL: preserve the raw document
curl --fail --location \
--user-agent 'AuthorizedMetadataCollector/1.0 (contact: data-owner@example.com)' \
--header 'Accept-Language: en-US,en;q=0.8' \
--connect-timeout 10 --max-time 30 \
'https://www.audible.com/pd/APPROVED-PRODUCT-URL' \
--output audible-product.html
Parse the saved file after retrieval so the raw response remains available for audit and parser updates. Do not use a loop to expand this into an unapproved crawl.
Node.js: retrieve and inspect JSON-LD
import { writeFile } from "node:fs/promises";
const url = "https://www.audible.com/pd/APPROVED-PRODUCT-URL";
const response = await fetch(url, {
headers: {
"user-agent": "AuthorizedMetadataCollector/1.0 (contact: data-owner@example.com)",
"accept-language": "en-US,en;q=0.8"
},
signal: AbortSignal.timeout(30000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const html = await response.text();
await writeFile("audible-product.html", html, "utf8");
const blocks = [...html.matchAll(/<script[^>]+type=["']application\\/ld\\+json["'][^>]*>([\\s\\S]*?)<\\/script>/gi)];
for (const block of blocks) {
try {
console.log(JSON.parse(block[1]));
} catch {
// Ignore malformed or non-product JSON-LD blocks.
}
}
Parsing strategy that survives markup changes
- Prefer JSON-LD. Look for a
Productobject and readname,offers.price,offers.priceCurrency,offers.availability, andaggregateRatingwhen present. - Map visible labels second. Use stable semantic labels for author, narrator, publisher, duration, series, and publication date. Avoid long CSS class names that are likely to change.
- Keep raw and normalized forms. Store the original price string, parsed decimal value, currency, locale, and retrieval timestamp. Never silently convert a missing price to zero.
- Model multiplicity. Authors, narrators, genres, series entries, and contributors should be arrays with ordering preserved.
- Validate identifiers. Check the expected shape of an ASIN or digital ISBN, but retain the original text when validation fails for review.
- Version your parser. Record a parser version with each output. Compare field presence over time and alert on sudden changes.
Locale, availability, and edge cases
- Marketplace differences: The same title can have different prices, currencies, availability, contributors, and descriptions by country. Include the hostname, language, and any authorized locale in your key.
- Missing JSON-LD: Treat it as a normal parse outcome. Save the document, use your approved visible-label fallback, and flag the record for review.
- Multiple offers: An
offersvalue may be an object or an array. Select only the offer type your authorization permits and retain its availability. - Unavailable titles: A title can remain identifiable while its price or purchase offer is absent. Represent absence as null with a reason, not as an invented value.
- Login or consent pages: Do not attempt to defeat them. Stop, record the response category, and contact the authorized owner.
- Dynamic content: Ratings and purchase panels may be rendered after the initial HTML. If your written authorization allows browser rendering, use it conservatively and retain evidence of the permitted method.
- Changes and deletions: A later response may remove a field. Keep the previous snapshot only for the retention period allowed by the agreement and provide a deletion path.
Rate limiting, reliability, and cost
Reliability starts with a small queue and an explicit rate limit. Use one request at a time unless the authorization specifies a higher rate. Add bounded retries only for transient network failures such as connection resets or 5xx responses; do not retry access denials, CAPTCHAs, 401/403 responses, or terms-related blocks. Use exponential backoff with jitter, for example 2, 4, and 8 seconds, and stop after a small maximum.
Cache a page only for the duration allowed by the authorization. Conditional requests can reduce bandwidth when supported, but a cache hit is not permission to retain data indefinitely. Log status code, response time, URL, locale, parser version, and failure category without logging credentials or private account data.
Direct HTTP retrieval is usually the least expensive transport, but engineering cost includes parser maintenance, retries, locale handling, monitoring, legal review, and deletion work. A managed retrieval provider can simplify rendering and retry infrastructure when your permission allows its use; the governing authorization and redistribution rights matter more than the transport.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 403 or access denied | Request is not authorized, rate is too high, or an access policy changed. | Stop retries, verify written permission, reduce rate, and contact the owner. |
| CAPTCHA or bot-check HTML | The service is asking for an interactive verification. | Do not bypass it. Record the event and use an approved feed or request permission for an allowed method. |
| JSON decode error | The JSON-LD block is malformed, escaped, or not a product object. | Catch parse errors, inspect the raw block, and select the correct @type. |
| Price is missing | The title is unavailable, the locale differs, or the offer is rendered dynamically. | Store null with a reason; check an authorized locale or browser method. |
| Wrong currency | A symbol was interpreted without marketplace context. | Use priceCurrency and store hostname and locale alongside the value. |
| Author or narrator is empty | The field is outside JSON-LD or has changed labels. | Use a semantic-label fallback, preserve the raw document, and update the parser under change control. |
| Timeouts | Network instability or a slow dynamic page. | Use a bounded timeout, one retry for transient errors, and a queue that records failures. |

Or skip the browser setup
If you need a clean visual record of an authorized Audible page rather than structured fields, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for all options. This is the shortest call:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.audible.com/pd/APPROVED-PRODUCT-URL -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://www.audible.com/pd/APPROVED-PRODUCT-URL"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://www.audible.com/pd/APPROVED-PRODUCT-URL' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports full-page capture, CSS-selector element capture, custom JavaScript and CSS, waits, blocking rules, custom headers and cookies, user agents, timezone and geolocation, caching with a chosen TTL, signed links, asynchronous jobs, bulk capture, PDFs, and an MCP server with take_screenshot, get_page_info, and capture_pdf for AI agents. Use those options only within the authorization governing the Audible pages.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.
FAQ
Is there an official public Audible scraping API?
The research available for this guide found unofficial documentation for an audible project with catalog-product endpoints and response groups, but that documentation is not proof of a supported public API. Confirm credentials, authorization, limits, and current behavior with Audible before using it.
Can I build a complete Audible catalog from product pages?
Only if your written permission explicitly covers systematic discovery, the relevant locales, fields, retention, and redistribution. A few approved URLs do not imply permission to crawl search results or create a database.
Should ratings be treated as permanent facts?
No. Ratings and review counts change, so store them as timestamped snapshots with marketplace and locale.
Can I reuse the extracted prices in a commercial application?
Do not assume so. Audible’s license language specifically restricts collection and use of product listings, descriptions, and prices. Obtain written permission or a licensed feed that grants the intended commercial rights.
What should I do when the markup changes?
Keep the raw response, flag missing fields, version the parser, and review the change against your authorization before deploying an update.


