{"id":"e13f3dd9-7780-459d-b63a-5746b04824d2","entityType":"agent","slug":"clawhub-ramius88-booksearch-api","name":"BeyondBSR - BookSearch API","canonicalUrl":"https://www.xpersona.co/agent/clawhub-ramius88-booksearch-api","canonicalPath":"/agent/clawhub-ramius88-booksearch-api","generatedAt":"2026-10-10T14:43:59.028Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":null},"description":"Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br... Skill: BeyondBSR - BookSearch API Owner: ramius88 Summary: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br... Tags: latest:1.0.9 Version history: v1.0.9 | 2026-06-03T11:57:49.641Z | user - Removed the skill-card.md file. - No changes to functionality or documentation content. v1.0.8 | 2026-06-01T13:57:00.217Z","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.4K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s176q0vwbyv9fyg5jsxfm2pjv1857303:booksearch-api","sourceUrl":"https://clawhub.ai/ramius88/booksearch-api","homepage":"https://clawhub.ai/ramius88/skills/booksearch-api","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/ramius88/booksearch-api","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/ramius88/skills/booksearch-api","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":null},"stars":null,"forks":null,"downloads":1438,"packageName":null,"latestVersion":"1.0.9","tractionLabel":"1.4K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T12:16:33.202Z","lastCrawledAt":"2026-10-10T12:16:33.202Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T12:16:33.202Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.9","createdAt":"2026-06-03T11:57:49.641Z","changelog":"- Removed the skill-card.md file. - No changes to functionality or documentation content.","fileCount":3,"zipByteSize":15109},{"version":"1.0.8","createdAt":"2026-06-01T13:57:00.217Z","changelog":"- Limited marketplace support is now specified: Only US (Amazon.com) and France (Amazon.fr) are currently available for data/search. - Documentation, endpoints, and request examples updated to clarify that only domainId 1 (US) and 4 (FR) return results. - Removed mention of support for other marketplaces; clearly marked as \"coming soon.\" - Skill no longer includes the file skill-card.md (removed). - Minor corrections to endpoint URLs and descriptive text for accuracy.","fileCount":3,"zipByteSize":14778},{"version":"1.0.7","createdAt":"2026-05-15T11:57:28.698Z","changelog":"No user-facing changes in this version. - No code or documentation changes detected. - Functionality and usage remain unchanged.","fileCount":3,"zipByteSize":14819},{"version":"1.0.6","createdAt":"2026-05-15T10:40:06.716Z","changelog":"**Category ancestor chains are now included in book search results for better market analysis and aggregation.** - Book-search responses now contain each book's full root→leaf Amazon category ancestor chain(s) inline. - Enables grouping or aggregating results by macro/sub category without additional API calls. - Updated description and usage guidance to reflect new category aggregation features. - Clarified that API access is in private beta and requires a BeyondBSR key.","fileCount":2,"zipByteSize":12691},{"version":"1.0.5","createdAt":"2026-05-11T09:18:32.724Z","changelog":"- Added support for Amazon category taxonomy browsing (browse node lookup, children, breadcrumbs, and search by name). - Expanded description and usage examples to include category discovery and category code lookup. - Documented four new endpoints for category exploration alongside existing book search and BSR history. - Clearly specified when to use category endpoints (e.g., resolving a category name to `catId` for book search). - Improved endpoint and feature documentation in SKILL.md for clarity and completeness.","fileCount":2,"zipByteSize":11060},{"version":"1.0.4","createdAt":"2026-05-05T13:13:50.791Z","changelog":"No user-facing changes detected in this version. - No file changes were detected between versions 1.0.3 and 1.0.4. - Behavior and documentation remain consistent with previous release.","fileCount":2,"zipByteSize":7902},{"version":"1.0.3","createdAt":"2026-04-28T13:56:10.765Z","changelog":"No user-visible changes in this version; no file changes detected.","fileCount":2,"zipByteSize":7679},{"version":"1.0.2","createdAt":"2026-04-27T10:28:53.566Z","changelog":"Summary: Adds support for retrieving the historical BSR (Best Sellers Rank) timeline for a single ASIN. - Introduced a new endpoint to fetch BSR history for a specific book (by domainId and ASIN) for the last N days. - Updated skill description and usage guidance to reflect single-ASIN BSR timeline support. - Clarified that price-history and review/rating timelines remain out of scope. - No breaking changes to the main book search endpoint or filter options.","fileCount":2,"zipByteSize":7209}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s176q0vwbyv9fyg5jsxfm2pjv1857303:booksearch-api","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T14:43:59.024Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ramius88-booksearch-api/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":null},"readme":"Skill: BeyondBSR - BookSearch API\n\nOwner: ramius88\n\nSummary: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br...\n\nTags: latest:1.0.9\n\nVersion history:\n\nv1.0.9 | 2026-06-03T11:57:49.641Z | user\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation content.\n\nv1.0.8 | 2026-06-01T13:57:00.217Z | user\n\n- Limited marketplace support is now specified: Only US (Amazon.com) and France (Amazon.fr) are currently available for data/search.\n- Documentation, endpoints, and request examples updated to clarify that only domainId 1 (US) and 4 (FR) return results.\n- Removed mention of support for other marketplaces; clearly marked as \"coming soon.\"\n- Skill no longer includes the file skill-card.md (removed).\n- Minor corrections to endpoint URLs and descriptive text for accuracy.\n\nv1.0.7 | 2026-05-15T11:57:28.698Z | user\n\nNo user-facing changes in this version.\n\n- No code or documentation changes detected.\n- Functionality and usage remain unchanged.\n\nv1.0.6 | 2026-05-15T10:40:06.716Z | user\n\n**Category ancestor chains are now included in book search results for better market analysis and aggregation.**\n\n- Book-search responses now contain each book's full root→leaf Amazon category ancestor chain(s) inline.\n- Enables grouping or aggregating results by macro/sub category without additional API calls.\n- Updated description and usage guidance to reflect new category aggregation features.\n- Clarified that API access is in private beta and requires a BeyondBSR key.\n\nv1.0.5 | 2026-05-11T09:18:32.724Z | user\n\n- Added support for Amazon category taxonomy browsing (browse node lookup, children, breadcrumbs, and search by name).\n- Expanded description and usage examples to include category discovery and category code lookup.\n- Documented four new endpoints for category exploration alongside existing book search and BSR history.\n- Clearly specified when to use category endpoints (e.g., resolving a category name to `catId` for book search).\n- Improved endpoint and feature documentation in SKILL.md for clarity and completeness.\n\nv1.0.4 | 2026-05-05T13:13:50.791Z | user\n\nNo user-facing changes detected in this version.\n\n- No file changes were detected between versions 1.0.3 and 1.0.4.\n- Behavior and documentation remain consistent with previous release.\n\nv1.0.3 | 2026-04-28T13:56:10.765Z | user\n\nNo user-visible changes in this version; no file changes detected.\n\nv1.0.2 | 2026-04-27T10:28:53.566Z | user\n\nSummary: Adds support for retrieving the historical BSR (Best Sellers Rank) timeline for a single ASIN.\n\n- Introduced a new endpoint to fetch BSR history for a specific book (by domainId and ASIN) for the last N days.\n- Updated skill description and usage guidance to reflect single-ASIN BSR timeline support.\n- Clarified that price-history and review/rating timelines remain out of scope.\n- No breaking changes to the main book search endpoint or filter options.\n\nv1.0.1 | 2026-04-20T08:11:29.673Z | user\n\nNo user-facing changes in this release.  \n- No file or documentation updates were detected.  \n- Functionality and API usage remain unchanged.\n\nv1.0.0 | 2026-04-20T07:57:22.212Z | user\n\nbooksearch-api 1.0.0\n\n- Initial release of booksearch-api skill.\n- Search Amazon KDP books on BeyondBSR public API with advanced filters (BSR, category, keyword, royalty, rating, reviews, date, binding, marketplace).\n- Supports KDP research scenarios like niche discovery, competitor analysis, and sales/revenue estimation.\n- Marketplace domain mapping for 7 Amazon countries.\n- Pagination and proper error handling for authentication.\n- Not for single-ASIN price/BSR history lookups.\n\nArchive index:\n\nArchive v1.0.9: 3 files, 15109 bytes\n\nFiles: skill-card.md (2299b), SKILL.md (42693b), _meta.json (133b)\n\nFile v1.0.9:SKILL.md\n\n---\nname: booksearch-api\ndescription: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (browse nodes) for any supported marketplace. Each book-search result now ships its full root → leaf category ancestor chain(s) inline, so the skill can aggregate market-opportunity reports by macro/sub category without follow-up calls. Use this skill whenever the user wants to discover, filter, or research self-published or traditionally published books on Amazon by BSR, category, keyword, royalty range, rating, reviews, publication date, binding type, or marketplace (currently only the US and FR Amazon marketplaces are populated with data — more coming soon); when the user wants the BSR timeline of a specific ASIN over the last N days; when the user wants to look up Amazon category codes (browse node IDs), walk the category tree (children, ancestors/breadcrumb), or search categories by name to use as filters in book search; or when the user wants to group/aggregate search results by macro category (e.g. \"how many Personal Finance opportunities, broken down by sub-category?\"). Typical intents include KDP niche research, low-competition book discovery, sales estimation, royalty/revenue projection, competitor analysis, paperback/hardcover filtering, bulk listing of books matching numeric/textual criteria, single-ASIN BSR history charts, resolving a human-readable category name (e.g. \"manga\", \"self-help\") into the Amazon `catId` to pass to `categoryIds` in book search, and producing category-grouped market-opportunity summaries from a single search response. Do not use for price-history timelines, review/rating timelines, or account/user data — only book search, BSR history, and category browsing are exposed.\nmetadata:\n  clawdbot:\n    requires:\n      env:\n        - BOOKSEARCH_API_KEY\n---\n\n# BookSearch API\n\n## ⚠️ API Access & Beta Program\n\nThe BeyondBSR BookSearch API is currently in **private beta**. This skill requires an API key (`BOOKSEARCH_API_KEY`) which is **not publicly available** at this time.\n\nUsers interested in accessing Amazon KDP book data (BSR history, reviews, categories, keyword research) can apply to the early adopter program by contacting **support@beyondbsr.com**. Requests are reviewed individually and approved keys are issued on a case-by-case basis.\n\nWithout a valid key, all endpoints below will return `401 Unauthorized`.\n\n---\n\nProgrammatic search over the BeyondBSR book catalogue, BSR history retrieval for a single book, and Amazon category taxonomy browsing. Six endpoints, JSON in / JSON out, API-key auth.\n\n## When to use this skill\n\nUse it when the user asks to:\n\n- Find books matching numeric filters: BSR range, rating, reviews count, royalty, page count age, publication recency.\n- Discover niches by keyword inclusion/exclusion or Amazon category IDs.\n- Filter by marketplace (currently US / Amazon.com and FR / Amazon.fr only).\n- Distinguish self-publishers from traditional publishers.\n- Estimate sales (daily / weekly / monthly / quarterly) and revenue per copy.\n- Compare BSR averages across multiple time windows (7d / 30d / 90d / 180d / 365d).\n- Retrieve the **BSR timeline** of a single book (by `domainId` + `asin`) over the last N days, e.g. for charting rank evolution.\n- **Resolve a category name into an Amazon `catId`** (browse node ID), look up a category's direct children, walk its breadcrumb up to the root, or list top-level book categories for a marketplace — to feed `categoryIds` into book search, or just to explore the taxonomy.\n\n**Do NOT use** for: price-history charts, review/rating timelines, account/user data, or anything not in the response schemas below. Those are out of scope.\n\n## Endpoints\n\n```\nPOST https://api.beyondbsr.com/api/v1/books/search\nGET  https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nGET  https://api.beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\nGET  https://api.beyondbsr.com/api/v1/categories/children?domainId={..}&catId={..}\nGET  https://api.beyondbsr.com/api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\nGET  https://api.beyondbsr.com/api/v1/categories/ancestors?domainId={..}&catId={..}\nContent-Type: application/json   (book search only)\nX-API-Key: $BOOKSEARCH_API_KEY\n```\n\n## Authentication\n\n- Read the key from the `BOOKSEARCH_API_KEY` env var. Format: `bbsr_live_<43-char-base64url>`.\n- **Never** print, echo, log, or include the key in any user-facing output. Never paste it into another tool's input.\n- On `401 Unauthorized`: do not retry. Report \"API key missing or invalid — check `BOOKSEARCH_API_KEY` env var\" and stop.\n- Send `X-API-Key` exactly once. Multi-valued headers are rejected.\n\n## Marketplace domains\n\n**At this time only two marketplaces are populated with data: the United States (`domainId=1`) and France (`domainId=4`).** Additional marketplaces are planned but not yet available.\n\nMap natural-language marketplace references (e.g. \"the US store\", \"Amazon.com\", \"amazon francia\", \"Amazon.fr\") to `domainId` using this table:\n\n| domainId | locale | country | name          |\n|----------|--------|---------|---------------|\n| 1        | com    | US      | United States |\n| 4        | fr     | FR      | France        |\n\nThe validator technically accepts `domainId` `1–12`, but **only `1` (US) and `4` (FR) return data right now**. If the user asks for any other marketplace (UK, Germany, Italy, Spain, Canada, Japan, etc.), tell them it is not currently supported — coming soon. Do not invent domain IDs.\n\n## Request schema\n\nAll fields are optional except `domainId`. Enums accept either the string name (e.g. `\"Weekly\"`) or the integer value.\n\n### Required\n\n| Field      | Type | Constraint                  |\n|------------|------|-----------------------------|\n| `domainId` | int  | US (`1`) or FR (`4`) — the only marketplaces with data (validator allows 1–12). |\n\n### BSR filters\n\n| Field      | Type        | Default   | Notes                                                                       |\n|------------|-------------|-----------|-----------------------------------------------------------------------------|\n| `bsrType`  | enum        | `Weekly`  | `Historical(-1)`, `Current(0)`, `Weekly(7)`, `Days30(8)`, `Days90(9)`, `Days180(10)`, `Days365(11)` |\n| `bsrMin`   | int?        | 1         | ≥ 1                                                                         |\n| `bsrMax`   | int?        | 100000    | ≥ 1, `bsrMin ≤ bsrMax`                                                      |\n| `bsrYear`  | short?      | null      | 2000–(current year + 1). Required together with `bsrMonth`. Use only with `bsrType=Historical`. |\n| `bsrMonth` | short?      | null      | 1–12. Required together with `bsrYear`.                                     |\n\n### Product filters\n\n| Field              | Type    | Default     | Notes                                              |\n|--------------------|---------|-------------|----------------------------------------------------|\n| `bindingType`      | enum?   | null (all)  | `All`, `Paperback`, `Hardcover`                    |\n| `publisherType`    | enum?   | `All`       | `All`, `SelfPublishersOnly`, `PublishersOnly`      |\n| `interiorType`     | enum?   | `BlackWhite`| `BlackWhite`, `FullColor`                          |\n| `vatType`          | enum?   | `Reduced`   | `Reduced`, `Standard`                              |\n| `includePreOrders` | bool?   | `false`     | —                                                  |\n\n### Quality filters\n\n| Field         | Type    | Constraint                         |\n|---------------|---------|------------------------------------|\n| `ratingMin`   | double? | 0.0–5.0, `ratingMin ≤ ratingMax`   |\n| `ratingMax`   | double? | 0.0–5.0                            |\n| `reviewsMin`  | int?    | ≥ 0, `reviewsMin ≤ reviewsMax`     |\n| `reviewsMax`  | int?    | ≥ 0                                |\n\n### Economic filters\n\n| Field                        | Type     | Notes                                                |\n|------------------------------|----------|------------------------------------------------------|\n| `royaltyMin`                 | decimal? | In currency units, **not** cents. `min ≤ max`.       |\n| `royaltyMax`                 | decimal? | —                                                    |\n| `monthsSincePublicationMin`  | int?     | `min ≤ max`                                          |\n| `monthsSincePublicationMax`  | int?     | —                                                    |\n\n### Discovery\n\n| Field             | Type     | Default | Constraint                                                  |\n|-------------------|----------|---------|-------------------------------------------------------------|\n| `includeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. **Each element is matched as a literal contiguous substring** (case-insensitive) against title, publisher and authors. Multiple elements are combined with **OR**. Pass multi-word phrases as a single element (`[\"small business taxes\"]`), NOT as separate tokens (`[\"small\",\"business\",\"taxes\"]`) — the latter would match any book containing just one of those words. |\n| `excludeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. Same matching semantics as `includeKeywords`: each element is a literal contiguous substring; books matching **any** element on title, publisher or authors are excluded. |\n| `categoryIds`     | long[]   | null    | ≤ 100 items, each > 0 (Amazon BrowseNode IDs). **Subtree-expanded**: passing a non-leaf node (e.g. depth-2 \"Quick & Easy\", `catId=17`) returns every book tagged with any descendant leaf — you do not need to enumerate leaves yourself. Pass any node from `/categories`, `/categories/search`, `/categories/children`, or `/categories/ancestors`. Mixing leaves and parents in one call is allowed (logical OR across the union of the expanded sets). Stale / unknown `catId`s collapse to no overlap and are silently dropped, not an error. |\n| `excludeFiction`  | bool?    | `true`  | When `true` (default), filters out fiction books — i.e. books whose categories all roll up to depth-2 ancestors flagged as fiction (Literature & Fiction, Romance, Mystery, Sci-Fi, Children's Books, Teen/YA, etc.) under the local Books root for the requested marketplace. A book is kept if **at least one** of its categories has a depth-2 ancestor flagged as non-fiction. Set `false` to include fiction. **Auto-disabled** when `categoryIds` is non-empty: explicit category intent overrides the fiction filter, otherwise passing a fiction category with the default would silently return zero results. |\n\n### Pagination\n\n| Field    | Type | Default | Range      |\n|----------|------|---------|------------|\n| `limit`  | int? | 100     | 1–300      |\n| `offset` | int? | 0       | 0–100000   |\n\n## Workflow\n\n1. **Marketplace** — Identify `domainId` from intent using the marketplace table. If ambiguous (e.g. \"Amazon\"), ask which country.\n2. **Translate** — Map every natural-language filter to its schema field. Don't guess enum values; use the table. Note: by default fiction is excluded (`excludeFiction=true`). If the user asks for fiction (e.g. \"romance\", \"mystery novels\", \"show me fiction too\") or a mixed catalog, set `excludeFiction: false` explicitly. Most KDP/low-content niche queries are non-fiction so the default is usually correct.\n3. **Pagination** — Default to `limit: 50`. Raise to 100–300 only if the user explicitly wants many results. Start with `offset: 0`.\n4. **Send** — POST the JSON body. Inspect the status code first.\n5. **Paginate if needed** — If `returnedCount == limit`, more results likely exist. Offer to fetch the next page with `offset += limit`.\n6. **Present** — Surface `title`, `asin`, `bsr`, sales/revenue estimates, and a clickable cover URL (see response notes).\n\n## Example request\n\n```json\n{\n  \"domainId\": 4,\n  \"bsrType\": \"Days90\",\n  \"bsrMin\": 1,\n  \"bsrMax\": 50000,\n  \"bindingType\": \"Paperback\",\n  \"publisherType\": \"SelfPublishersOnly\",\n  \"ratingMin\": 4.0,\n  \"reviewsMin\": 10,\n  \"interiorType\": \"BlackWhite\",\n  \"vatType\": \"Reduced\",\n  \"royaltyMin\": 2.50,\n  \"royaltyMax\": 15.00,\n  \"monthsSincePublicationMax\": 24,\n  \"includePreOrders\": false,\n  \"includeKeywords\": [\"journal\", \"notebook\"],\n  \"excludeKeywords\": [\"coloring\"],\n  \"categoryIds\": [266162, 3248921],\n  \"excludeFiction\": false,\n  \"limit\": 50,\n  \"offset\": 0\n}\n```\n\n## Example cURL\n\n```bash\ncurl -X POST \"https://api.beyondbsr.com/api/v1/books/search\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -d '{\n    \"domainId\": 4,\n    \"bsrType\": \"Days90\",\n    \"bsrMax\": 50000,\n    \"bindingType\": \"Paperback\",\n    \"publisherType\": \"SelfPublishersOnly\",\n    \"ratingMin\": 4.0,\n    \"limit\": 50\n  }'\n```\n\n## Response schema\n\n### Envelope — `BookSearchApiResponse`\n\n| Field           | Type    | Notes                                                              |\n|-----------------|---------|--------------------------------------------------------------------|\n| `returnedCount` | int     | Items in **this page**. NOT a total-match count (no total exposed).|\n| `limit`         | int     | Effective limit applied (capped at 300).                           |\n| `offset`        | int     | Effective offset applied.                                          |\n| `results`       | array   | `BookSearchResultDto[]`. Empty if no match.                        |\n\n### Result item — `BookSearchResultDto` (most relevant fields)\n\n| Field                | Type     | Notes                                                                                  |\n|----------------------|----------|----------------------------------------------------------------------------------------|\n| `id`                 | long     | Internal book ID.                                                                      |\n| `asin`               | string   | 10-char Amazon ASIN.                                                                   |\n| `title`              | string   | Full title.                                                                            |\n| `authors`            | string?  | Comma-separated, ordered by `display_order`.                                           |\n| `publicationDate`    | datetime?| Nullable.                                                                              |\n| `publisher`          | string?  | Manufacturer/publisher name.                                                           |\n| `coverImageFilename` | string?  | Build URL: `https://m.media-amazon.com/images/I/{filename}`.                           |\n| `imageFilenames`     | string[] | All carousel images (same URL pattern).                                                |\n| `rating`             | double   | 0.0–5.0.                                                                               |\n| `reviews`            | int      | Total reviews.                                                                         |\n| `bsr`                | int      | **The column matching the requested `bsrType`** — i.e. the value filtered/sorted on.   |\n| `bsrCurrent`         | int?     | Latest snapshot BSR, regardless of `bsrType`.                                          |\n| `avgBsr7d`           | int?     | Always populated.                                                                      |\n| `avgBsr30d`          | int?     | Always populated.                                                                      |\n| `avgBsr90d`          | int?     | Always populated.                                                                      |\n| `avgBsr180d`         | int?     | Always populated.                                                                      |\n| `avgBsr365d`         | int?     | Always populated.                                                                      |\n| `pageCount`          | int?     | —                                                                                      |\n| `priceCents`         | int?     | List price (MSRP) in cents.                                                            |\n| `dailyEstimate`      | decimal  | Estimated copies/day from BSR model.                                                   |\n| `weeklyEstimate`     | decimal  | Estimated copies/week.                                                                 |\n| `monthlyEstimate`    | decimal  | Estimated copies/month.                                                                |\n| `quarterlyEstimate`  | decimal  | Copies/quarter (needs ≥ 13 weeks of data).                                             |\n| `royalty*Cents`      | int?     | Per-copy royalty in cents. 4 combinations: B/W or Color × Reduced or Standard VAT.     |\n| `*Revenue*Cents`     | long?    | Derived = estimate × royalty. 16 fields total: {daily,weekly,monthly,quarterly} × {Black,Color} × {Reduced,Standard}. |\n| `binding`            | string?  | Localised label (e.g. `\"Paperback\"`, `\"Hardcover\"`, `\"Non-standard\"`).                 |\n| `dimensions`         | string?  | Formatted, prefixed by binding. Example: `\"Paperback: 152 x 8 x 229 mm\"`.              |\n| `trim/spineWidthMm`  | int?     | Trim/spine measurements.                                                               |\n| `categories`         | long[]   | Raw leaf Amazon `cat_id`s the book is tagged with (verbatim from the database). Preserves original tag order. May be empty for books still being enriched. |\n| `categoryPaths`      | `Array<Array<{catId:long,name:string,depth:int}>>` | One inner array per entry in `categories`, each is the full root → leaf ancestor chain sorted by `depth` ascending. Use this to aggregate / group books by macro or sub category in a single round-trip without calling `/categories/ancestors` per leaf. |\n| `subcategoryRanks`   | `Array<{catId:long,rank:int,name:string}>` | Per-category Best Sellers Rank of the book inside each subcategory it is listed in (Amazon's \"#1 in <category>\" data). `catId` is the Amazon browse node, `rank` is the position within that node, `name` is the localised category label. Ordered as returned by Amazon (best/most-specific first). May be empty `[]` for books not yet enriched. Distinct from the top-level `bsr`/`bsrCurrent`, which are the overall Books-store rank. |\n| `frequentlyBoughtTogether` | `string[]` | ASINs of products Amazon surfaces as frequently bought together with this book. Use for competitor / cross-sell discovery (feed each ASIN back into `bsr-history` or a `includeKeywords`/`asin`-targeted search). May be empty `[]`. Not every book has this data — populated for a subset of titles. |\n\n**Important caveats**\n\n- All royalty and revenue fields are in **cents** — divide by 100 before showing currency.\n- `bsr` ≠ `bsrCurrent`. `bsr` is whatever column was chosen by `bsrType`; `bsrCurrent` is always the latest snapshot.\n- In `bsrType=Historical` mode, the `avgBsrXd` averages come from the current snapshot table while `bsr` itself is the historical month value — there is a documented temporal asymmetry inside the same response. Mention this if the user is doing a strict historical analysis.\n- `categoryPaths.length === categories.length` for a healthy dataset. If a leaf `cat_id` has been deleted upstream after the book was indexed, the corresponding inner array is silently dropped (never null) — so `categoryPaths.length` may be ≤ `categories.length`. The `categories` array always reflects the original tag set.\n- Both `categories` and `categoryPaths` may be **empty arrays `[]`** (never `null`) for books still being enriched or whose taxonomy snapshot hasn't propagated yet. Aggregation code must skip these books rather than fail on missing chains.\n\n### Aggregating results by category\n\nEach `categoryPaths` inner array is sorted by `depth` ascending. The taxonomy under the Books root is consistent across marketplaces (only the labels are localised):\n\n| Depth | Role | US example | IT example |\n|-------|------|------------|-----------|\n| 0 | Books root | `Books` | `Libri` |\n| 1 | Container shell | `Subjects` | `Categorie` |\n| 2 | **Top-level subject** (\"macro\") | `Self-Help`, `Cookbooks, Food & Wine`, `Crafts, Hobbies & Home` | `Cucina, casa e giardinaggio` |\n| 3 | **Sub-category** | `Crafts & Hobbies`, `Christian Books & Bibles` | — |\n| 4-5 | **Niche / micro-niche** (KDP-relevant) | `Coloring Books for Grown-Ups`, `Bible Study & Reference` | — |\n\nTo produce a market-opportunity report, group on the **`catId` at the desired depth**, not the `name` (names are locale-dependent; `catId` is stable). For KDP niche research the interesting depths are **3 and 4**: depth 2 is usually too broad (e.g. \"Crafts & Hobbies\" alone covers thousands of books), while depth 3-4 isolates real nicchie (\"Coloring Books for Grown-Ups\", \"Word Search\", \"Christian Living\").\n\nPattern — nested macro → sub-niche histogram with revenue rollup, from one search response:\n\n```js\n// macro (depth=2) -> { name, books, subniches: Map<catId, { name, books, monthlyRevCents }> }\nconst report = new Map();\n\nfor (const book of response.results) {\n  const seenMacros  = new Set();   // dedupe within the same book\n  const seenNiches  = new Set();\n\n  for (const chain of book.categoryPaths) {        // may be [] for un-enriched books\n    const macro = chain.find(n => n.depth === 2);\n    const niche = chain.find(n => n.depth === 3) ?? chain.find(n => n.depth === 4);\n    if (!macro) continue;\n\n    if (!seenMacros.has(macro.catId)) {\n      seenMacros.add(macro.catId);\n      const m = report.get(macro.catId) ?? { name: macro.name, books: 0, subniches: new Map() };\n      m.books++;\n      report.set(macro.catId, m);\n    }\n    if (niche && !seenNiches.has(niche.catId)) {\n      seenNiches.add(niche.catId);\n      const m = report.get(macro.catId);\n      const n = m.subniches.get(niche.catId) ?? { name: niche.name, books: 0, monthlyRevCents: 0 };\n      n.books++;\n      // pick whichever revenue field matches the user's interior/VAT context\n      n.monthlyRevCents += book.monthlyRevenueBlackReducedVatCents ?? 0;\n      m.subniches.set(niche.catId, n);\n    }\n  }\n}\n```\n\nSample fragment of a result with two categories (US, depth-1 is always `Subjects` — depth-2 is the macro):\n\n```json\n{\n  \"asin\": \"1234567890\",\n  \"title\": \"Quick Weeknight Dinners\",\n  \"categories\": [4259, 9876],\n  \"categoryPaths\": [\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",             \"depth\": 1 },\n      { \"catId\": 6,      \"name\": \"Cookbooks, Food & Wine\",\"depth\": 2 },\n      { \"catId\": 17,     \"name\": \"Quick & Easy\",         \"depth\": 3 },\n      { \"catId\": 4259,   \"name\": \"General\",              \"depth\": 4 }\n    ],\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                       \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",                    \"depth\": 1 },\n      { \"catId\": 10,     \"name\": \"Health, Fitness & Dieting\",   \"depth\": 2 },\n      { \"catId\": 9876,   \"name\": \"Diet & Weight Loss\",          \"depth\": 3 }\n    ]\n  ]\n}\n```\n\n**One-shot aggregation tip.** When the goal is a single histogram/report (as opposed to interactive paging), call `POST /books/search` with `limit=300` (the max). A 300-result response is ~850 KB and returns in ~200-350 ms in production — almost always cheaper than paginating. Only fall back to paged fetches if more than 300 books matter for the report (rare for niche analysis).\n\n## BSR History endpoint\n\nSingle-ASIN BSR timeline. Returns raw snapshots from the time-series store, ordered ascending by `recordedAt`.\n\n```\nGET https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### When to use\n\n- The user has a specific ASIN and wants its BSR over time (chart, drill-down, sanity-check the search-time `avgBsrXd` averages).\n- The user wants to verify a book's recent rank trajectory (e.g. \"is it gaining or losing visibility?\").\n\nIf the user has filter criteria but no specific ASIN, use `POST /search` first, then call this endpoint per ASIN of interest.\n\n### Path & query parameters\n\n| Param      | In    | Type   | Required | Constraint                                                                 |\n|------------|-------|--------|----------|----------------------------------------------------------------------------|\n| `domainId` | path  | int    | yes      | US (`1`) or FR (`4`) — see Marketplace domains table (validator allows 1–12). |\n| `asin`     | path  | string | yes      | Exactly 10 chars, regex `^[A-Z0-9]{10}$` (uppercase letters / digits only). |\n| `days`     | query | int?   | no       | 1–365. Default `365`. Window is `[now - days, now]` UTC.                   |\n\n### Example request\n\n```\nGET /api/v1/books/1/1635864348/bsr-history?days=90\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### Example cURL\n\n```bash\ncurl -X GET \"https://api.beyondbsr.com/api/v1/books/1/1635864348/bsr-history?days=90\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -H \"Accept: application/json\"\n```\n\n### Response schema — `BookBsrHistoryApiResponse`\n\n| Field         | Type        | Notes                                                              |\n|---------------|-------------|--------------------------------------------------------------------|\n| `asin`        | string      | Echoed from request.                                               |\n| `domainId`    | int         | Echoed from request.                                               |\n| `fromUtc`     | datetime    | Window start (`now - days`), UTC.                                  |\n| `toUtc`       | datetime    | Window end (`now`), UTC.                                           |\n| `pointCount`  | int         | Number of BSR snapshots in `points`.                               |\n| `points`      | array       | `BsrPointDto[]` ordered ascending by `recordedAt`.                 |\n\n`BsrPointDto`:\n\n| Field              | Type      | Notes                                                          |\n|--------------------|-----------|----------------------------------------------------------------|\n| `recordedAt`       | datetime  | UTC timestamp of the snapshot.                                 |\n| `bestSellersRank`  | int?      | BSR at that timestamp. May be null if Keepa returned no rank.  |\n\nExample body:\n\n```json\n{\n  \"asin\": \"1635864348\",\n  \"domainId\": 1,\n  \"fromUtc\": \"2026-01-27T00:00:00Z\",\n  \"toUtc\": \"2026-04-27T00:00:00Z\",\n  \"pointCount\": 412,\n  \"points\": [\n    { \"recordedAt\": \"2026-01-27T03:14:00Z\", \"bestSellersRank\": 1234 },\n    { \"recordedAt\": \"2026-01-27T15:02:00Z\", \"bestSellersRank\": 1218 }\n  ]\n}\n```\n\n### Status codes (BSR history specific)\n\n| Code | Meaning                       | Notes                                                                 |\n|------|-------------------------------|-----------------------------------------------------------------------|\n| 200  | OK                            | `pointCount` may be 0 if no snapshots exist in the window.            |\n| 400  | Validation failed             | `ValidationProblemDetails`. Common causes: `days` out of range, `asin` wrong format, `domainId` out of `[1,12]`. |\n| 404  | ASIN not found for that domain| `ProblemDetails` JSON (`{title:\"Not Found\",status:404,...}`). The book is not tracked in BeyondBSR for this marketplace. Tell the user — do not retry with the same pair. |\n| 401 / 429 / 500 | See generic table below.            |\n\n### Caveats\n\n- Granularity is **raw**: Keepa snapshots arrive 1–4× per day on actively-tracked books. A 365-day window typically yields 365–1460 points. No daily aggregation is applied.\n- The endpoint returns only `(recordedAt, bestSellersRank)`. Use `POST /search` if you also need rating/review/price data.\n- ASIN is case-sensitive — must be uppercase. `1635864348` (all digits) is valid.\n- Default 365 days is the cap. Longer histories are not exposed; do not retry with `days > 365`.\n\n## Categories endpoints\n\nThe four endpoints under `/api/v1/categories` expose the Amazon taxonomy (browse nodes) BeyondBSR has ingested per marketplace. They share the same API-key auth, rate limit (`api-key` policy), and `domainId` semantics as book search. All four are `GET`, JSON out, no request body.\n\n### Use cases\n\n- **Resolve a name → `catId`** to pass into `POST /books/search` `categoryIds`. Example: user asks for \"manga\" books on US → call `/categories/search?domainId=1&q=manga` to get candidate cat_ids, then feed the chosen ones into `categoryIds`.\n- **Top-level book categories** of a marketplace (e.g. \"list the main Books categories on Amazon.fr\"): `GET /categories?domainId=4&depth=2`.\n- **Walk down the tree** from a known node (e.g. \"what's under Cookbooks?\"): `GET /categories/children?domainId=1&catId=6`.\n- **Walk up the tree / breadcrumb** for a leaf node (e.g. \"where does cat 4142740011 sit?\"): `GET /categories/ancestors?domainId=1&catId=4142740011` → returns root → … → node, ordered by depth.\n\n### Shared response object — `CategoryBrowseNodeDto`\n\n| Field             | Type     | Notes                                                                                       |\n|-------------------|----------|---------------------------------------------------------------------------------------------|\n| `catId`           | long     | Amazon browse node ID. **This is the value to pass into `categoryIds` in book search.**     |\n| `parentCatId`     | long?    | Parent's `catId`. `null` for roots.                                                         |\n| `rootCatId`       | long     | Top-level ancestor's `catId` (e.g. the Books root for the marketplace).                     |\n| `name`            | string   | Localised name in the marketplace's language.                                               |\n| `contextFreeName` | string?  | Name without parent context (Keepa-provided). May be null.                                  |\n| `depth`           | int      | 0 = root. Top-level book categories are typically depth 2 (root → Categorie → top node).    |\n| `isFiction`       | bool     | Internal flag used by `excludeFiction` in book search. Only meaningful at depth=2 under Books root. |\n| `productCount`    | int?     | Approximate number of products in that node (Keepa-reported). May be null.                  |\n\n### 1. List categories at a depth (top-level Books browser)\n\n```\nGET /api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\n```\n\n| Param            | Type  | Default | Notes                                                                                                                                  |\n|------------------|-------|---------|----------------------------------------------------------------------------------------------------------------------------------------|\n| `domainId`       | int   | —       | Required. Marketplace ID (1-12, but only `1` US and `4` FR return data).                                                                |\n| `depth`          | int?  | 2       | Hierarchy level. 0 = root. 2 = top-level book categories (\"Self-Help\", \"Cookbooks…\", etc.). Allowed 0-5.                               |\n| `includeFiction` | bool? | `false` | Whether to include nodes flagged as fiction (Literature & Fiction, Romance, Sci-Fi, Children's Books, Teen/YA, Comics, etc.).          |\n\n**Filters applied automatically (do not appear in params):** only browse nodes (`isBrowseNode=true`), and only nodes whose root is the **Books** root of the marketplace — i.e. non-book trees (toys, electronics) are excluded. Use this endpoint to enumerate the canonical KDP-relevant taxonomy.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nResponse envelope — `CategoriesApiResponse`:\n\n| Field           | Type    | Notes                                |\n|-----------------|---------|--------------------------------------|\n| `domainId`      | int     | Echo.                                |\n| `depth`         | int     | Echo (resolved default if omitted).  |\n| `returnedCount` | int     | Number of items in `results`.        |\n| `results`       | array   | `CategoryBrowseNodeDto[]`.           |\n\n### 2. Direct children of a category\n\n```\nGET /api/v1/categories/children?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                                            |\n|------------|------|----------------------------------------------------------------------------------|\n| `domainId` | int  | Required.                                                                        |\n| `catId`    | long | Required. Parent category's Amazon browse node ID.                               |\n\n**No implicit filters.** Returns ALL active direct children of the parent — including non-browse nodes and fiction nodes. Use it for true tree navigation regardless of book/non-book context.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nReturns **404** if `catId` does not exist for the given marketplace. Do not retry on 404.\n\nResponse envelope — `CategoryChildrenApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `parentCatId`   | long   | Echo of the input `catId`.           |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n### 3. Search categories by name\n\n```\nGET /api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\n```\n\n| Param      | Type    | Default | Notes                                                                                                              |\n|------------|---------|---------|--------------------------------------------------------------------------------------------------------------------|\n| `domainId` | int     | —       | Required.                                                                                                          |\n| `q`        | string  | —       | Required. Case-insensitive substring match on `name`. Min **3** chars, max 100. Wildcards (`%`, `_`, `\\`) are treated as literals (escaped server-side). |\n| `limit`    | int?    | 50      | 1-200. Pushed down to SQL.                                                                                         |\n\n**No implicit filters.** Searches across the ENTIRE category tree of the marketplace (not just Books) — so a query like \"Sports\" on Amazon.com will surface both \"Sports & Outdoors\" (under Books) and the toy/apparel \"Sports\" nodes. The agent should filter client-side by `rootCatId` if the user only wants book categories.\n\nOrder: `productCount` DESC, then `depth` ASC, then `name` ASC.\n\nResponse envelope — `CategorySearchApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `query`         | string | Echo of `q`.                         |\n| `limit`         | int    | Effective limit applied.             |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n**Tip:** if the user-typed term is broad (e.g. \"fiction\") and the default `limit=50` truncates likely candidates, raise `limit` to 200. If still not enough, refine the term or use `/categories?depth=2` instead.\n\n### 4. Ancestors / breadcrumb of a category\n\n```\nGET /api/v1/categories/ancestors?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                  |\n|------------|------|--------------------------------------------------------|\n| `domainId` | int  | Required.                                              |\n| `catId`    | long | Required. Category whose breadcrumb to retrieve.       |\n\nReturns the full ancestor chain **including the node itself**, ordered by `depth` ASC. No implicit filters (intermediate non-browse / promotional nodes are included).\n\nReturns **404** if `catId` does not exist for the marketplace.\n\nResponse envelope — `CategoryAncestorsApiResponse`:\n\n| Field           | Type   | Notes                                                                |\n|-----------------|--------|----------------------------------------------------------------------|\n| `domainId`      | int    | Echo.                                                                |\n| `catId`         | long   | Echo.                                                                |\n| `returnedCount` | int    | Length of the chain (includes the requested node).                   |\n| `results`       | array  | `CategoryBrowseNodeDto[]` from root (depth=0) to the node itself.    |\n\n### Example workflows\n\n**Resolve \"manga\" on Amazon.com then search books** (replace `<catId>` with a candidate returned by step 1):\n\n```bash\n# 1. find candidate cat_ids\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/search?domainId=1&q=manga&limit=10\"\n\n# 2. inspect the breadcrumb of a candidate to confirm it's under Books\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/ancestors?domainId=1&catId=<catId>\"\n\n# 3. drill down children if needed\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/children?domainId=1&catId=<catId>\"\n\n# 4. plug the chosen cat_id(s) into book search\ncurl -X POST -H \"X-API-Key: $BOOKSEARCH_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"domainId\":1,\"categoryIds\":[<catId>],\"limit\":50}' \\\n  \"https://api.beyondbsr.com/api/v1/books/search\"\n```\n\n**List top-level Books categories on Amazon.com (non-fiction only, default):**\n\n```bash\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories?domainId=1&depth=2\"\n```\n\n### Status codes (categories endpoints)\n\n| Code | Meaning                          | Notes                                                                                       |\n|------|----------------------------------|---------------------------------------------------------------------------------------------|\n| 200  | OK                               | `results` may be empty (e.g. no children, no matches, marketplace not seeded).              |\n| 400  | Validation failed                | `ValidationProblemDetails`. Common causes: `domainId` out of range, `q` < 3 chars, `limit` out of 1-200, `depth` out of 0-5. |\n| 401  | Auth failed                      | Same handling as book search — stop, report misconfigured key.                              |\n| 404  | `catId` not found for `domainId` | `ProblemDetails` JSON (`{title:\"Not Found\",status:404,...}`). Only on `/children` and `/ancestors`. Do not retry the same pair. |\n| 429  | Rate limit                       | Honour `Retry-After`. Same key budget as book search (30 req/min).                          |\n| 500  | Server error                     | Retry once, then escalate.                                                                  |\n\n### Caveats\n\n- **Read-only.** No POST/PUT/DELETE on categories.\n- **`catId` vs internal id.** The wire contract exposes only Amazon's `catId` (browse node ID). The internal database `id` is never surfaced and is not interchangeable.\n- **`depth=2` ≠ \"top-level under Books\" universally.** Most marketplaces seed Books root at depth=0 → \"Categorie/Categories\" at depth=1 → top nodes at depth=2. If `/categories?depth=2` returns unexpectedly few rows for a marketplace, retry with `depth=1`.\n- **`excludeFiction` semantics propagate.** A node's `isFiction=true` here is exactly what `excludeFiction` in book search rolls up on. If a user complains that a fiction sub-genre isn't being excluded, the agent can spot-check via `/categories/ancestors?catId=…` whether the depth-2 ancestor is flagged.\n- **Children endpoint includes non-browse nodes.** Some Amazon nodes are promotional or grouping shells (`isBrowseNode=false`). They cannot be used as `categoryIds` filters in book search. Skip them or warn the user.\n\n## Status codes & error handling\n\n| Code | Meaning                | Body                                       | Agent action                                                                  |\n|------|------------------------|--------------------------------------------|-------------------------------------------------------------------------------|\n| 200  | OK (may be empty list) | `BookSearchApiResponse`                    | Parse `results`. Empty array = no matches, not an error.                      |\n| 400  | Validation failed      | `ValidationProblemDetails` (RFC 7807)      | Read the `errors` map, fix the body, do not retry blindly. Surface the issue. |\n| 401  | Auth failed            | empty + `WWW-Authenticate: ApiKey realm=\"BeyondBSR\"` | Stop. Report misconfigured `BOOKSEARCH_API_KEY`. Do not retry.       |\n| 429  | Rate limit exceeded    | `\"Rate limit exceeded. Please try again later.\"` + `Retry-After` header | Honour `Retry-After`. Back off. Do not hammer the endpoint. |\n| 500  | Unhandled server error | `ProblemDetails` JSON                      | Retry once after a few seconds. If it persists, escalate to the user.         |\n\n### Example 400 body\n\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.1\",\n  \"title\": \"One or more validation errors occurred.\",\n  \"status\": 400,\n  \"errors\": {\n    \"DomainId\": [\"DomainId must be between 1 and 12.\"],\n    \"Limit\": [\"Limit must be between 1 and 300.\"]\n  }\n}\n```\n\n## Sorting & pagination notes\n\n- Default sort: `ORDER BY <chosen BSR column> ASC` (lower BSR = better seller appears first).\n- Historical mode (`bsrType=Historical` + `bsrYear`/`bsrMonth`): sorted by the historical monthly BSR rank ASC.\n- Tie-breaker is non-deterministic — books with identical BSR may appear in different orders across page fetches. Warn the user if they need a perfectly stable ordering.\n- No `orderBy` parameter is exposed.\n\n## Limits\n\n- **Rate limit**: 30 requests/minute per API key. Shared across all callers using the same key.\n- **Body size**: 128 KB max.\n- **Page size**: 300 results max per request (`limit ≤ 300`).\n- **Offset cap**: 100,000 (deep pagination beyond this is not supported — refine filters instead).\n\nFile v1.0.9:_meta.json\n\n{\n  \"ownerId\": \"kn71bkxxw77zyvcjdxzqpwcs658570bb\",\n  \"slug\": \"booksearch-api\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1780487869641\n}\n\nFile v1.0.9:skill-card.md\n\n## Description:\n\nSearches the BeyondBSR public API for Amazon KDP books, retrieves single-ASIN BSR history, and browses Amazon category taxonomy for supported marketplaces.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ramius88](https://clawhub.ai/user/ramius88)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users and developers use this skill for KDP niche research, competitor analysis, sales and royalty estimates, BSR history checks, and Amazon category lookup through the BeyondBSR API.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires a private beta API key and sends book-search queries to api.beyondbsr.com.\n\nMitigation: Install only when the publisher is trusted, keep BOOKSEARCH_API_KEY out of user-facing output, and avoid exposing it in logs or screenshots.\n\nRisk: The skill depends on external API availability, authorization, rate limits, and marketplace coverage.\n\nMitigation: Handle 401, 429, 404, and validation errors as documented, and limit use to populated US and FR marketplace data unless newer server evidence says otherwise.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ramius88/skills/booksearch-api)\n- [Publisher Profile](https://clawhub.ai/user/ramius88)\n- [BeyondBSR Book Search API](https://api.beyondbsr.com/api/v1/books/search)\n- [BeyondBSR BSR History API](https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365})\n- [BeyondBSR Categories API](https://api.beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false})\n\n## Skill Output:\n\n**Output Type(s):** [API Calls, Markdown, Code, Shell commands, Guidance]\n\n**Output Format:** [Markdown with JSON and shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include API request bodies, parsed result summaries, BSR timelines, category mappings, and market-opportunity analysis.]\n\n## Skill Version(s):\n\n1.0.9 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.0.8: 3 files, 14778 bytes\n\nFiles: skill-card.md (2551b), SKILL.md (41766b), _meta.json (133b)\n\nFile v1.0.8:SKILL.md\n\n---\nname: booksearch-api\ndescription: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (browse nodes) for any supported marketplace. Each book-search result now ships its full root → leaf category ancestor chain(s) inline, so the skill can aggregate market-opportunity reports by macro/sub category without follow-up calls. Use this skill whenever the user wants to discover, filter, or research self-published or traditionally published books on Amazon by BSR, category, keyword, royalty range, rating, reviews, publication date, binding type, or marketplace (currently only the US and FR Amazon marketplaces are populated with data — more coming soon); when the user wants the BSR timeline of a specific ASIN over the last N days; when the user wants to look up Amazon category codes (browse node IDs), walk the category tree (children, ancestors/breadcrumb), or search categories by name to use as filters in book search; or when the user wants to group/aggregate search results by macro category (e.g. \"how many Personal Finance opportunities, broken down by sub-category?\"). Typical intents include KDP niche research, low-competition book discovery, sales estimation, royalty/revenue projection, competitor analysis, paperback/hardcover filtering, bulk listing of books matching numeric/textual criteria, single-ASIN BSR history charts, resolving a human-readable category name (e.g. \"manga\", \"self-help\") into the Amazon `catId` to pass to `categoryIds` in book search, and producing category-grouped market-opportunity summaries from a single search response. Do not use for price-history timelines, review/rating timelines, or account/user data — only book search, BSR history, and category browsing are exposed.\nmetadata:\n  clawdbot:\n    requires:\n      env:\n        - BOOKSEARCH_API_KEY\n---\n\n# BookSearch API\n\n## ⚠️ API Access & Beta Program\n\nThe BeyondBSR BookSearch API is currently in **private beta**. This skill requires an API key (`BOOKSEARCH_API_KEY`) which is **not publicly available** at this time.\n\nUsers interested in accessing Amazon KDP book data (BSR history, reviews, categories, keyword research) can apply to the early adopter program by contacting **support@beyondbsr.com**. Requests are reviewed individually and approved keys are issued on a case-by-case basis.\n\nWithout a valid key, all endpoints below will return `401 Unauthorized`.\n\n---\n\nProgrammatic search over the BeyondBSR book catalogue, BSR history retrieval for a single book, and Amazon category taxonomy browsing. Six endpoints, JSON in / JSON out, API-key auth.\n\n## When to use this skill\n\nUse it when the user asks to:\n\n- Find books matching numeric filters: BSR range, rating, reviews count, royalty, page count age, publication recency.\n- Discover niches by keyword inclusion/exclusion or Amazon category IDs.\n- Filter by marketplace (currently US / Amazon.com and FR / Amazon.fr only).\n- Distinguish self-publishers from traditional publishers.\n- Estimate sales (daily / weekly / monthly / quarterly) and revenue per copy.\n- Compare BSR averages across multiple time windows (7d / 30d / 90d / 180d / 365d).\n- Retrieve the **BSR timeline** of a single book (by `domainId` + `asin`) over the last N days, e.g. for charting rank evolution.\n- **Resolve a category name into an Amazon `catId`** (browse node ID), look up a category's direct children, walk its breadcrumb up to the root, or list top-level book categories for a marketplace — to feed `categoryIds` into book search, or just to explore the taxonomy.\n\n**Do NOT use** for: price-history charts, review/rating timelines, account/user data, or anything not in the response schemas below. Those are out of scope.\n\n## Endpoints\n\n```\nPOST https://api.beyondbsr.com/api/v1/books/search\nGET  https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nGET  https://api.beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\nGET  https://api.beyondbsr.com/api/v1/categories/children?domainId={..}&catId={..}\nGET  https://api.beyondbsr.com/api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\nGET  https://api.beyondbsr.com/api/v1/categories/ancestors?domainId={..}&catId={..}\nContent-Type: application/json   (book search only)\nX-API-Key: $BOOKSEARCH_API_KEY\n```\n\n## Authentication\n\n- Read the key from the `BOOKSEARCH_API_KEY` env var. Format: `bbsr_live_<43-char-base64url>`.\n- **Never** print, echo, log, or include the key in any user-facing output. Never paste it into another tool's input.\n- On `401 Unauthorized`: do not retry. Report \"API key missing or invalid — check `BOOKSEARCH_API_KEY` env var\" and stop.\n- Send `X-API-Key` exactly once. Multi-valued headers are rejected.\n\n## Marketplace domains\n\n**At this time only two marketplaces are populated with data: the United States (`domainId=1`) and France (`domainId=4`).** Additional marketplaces are planned but not yet available.\n\nMap natural-language marketplace references (e.g. \"the US store\", \"Amazon.com\", \"amazon francia\", \"Amazon.fr\") to `domainId` using this table:\n\n| domainId | locale | country | name          |\n|----------|--------|---------|---------------|\n| 1        | com    | US      | United States |\n| 4        | fr     | FR      | France        |\n\nThe validator technically accepts `domainId` `1–12`, but **only `1` (US) and `4` (FR) return data right now**. If the user asks for any other marketplace (UK, Germany, Italy, Spain, Canada, Japan, etc.), tell them it is not currently supported — coming soon. Do not invent domain IDs.\n\n## Request schema\n\nAll fields are optional except `domainId`. Enums accept either the string name (e.g. `\"Weekly\"`) or the integer value.\n\n### Required\n\n| Field      | Type | Constraint                  |\n|------------|------|-----------------------------|\n| `domainId` | int  | US (`1`) or FR (`4`) — the only marketplaces with data (validator allows 1–12). |\n\n### BSR filters\n\n| Field      | Type        | Default   | Notes                                                                       |\n|------------|-------------|-----------|-----------------------------------------------------------------------------|\n| `bsrType`  | enum        | `Weekly`  | `Historical(-1)`, `Current(0)`, `Weekly(7)`, `Days30(8)`, `Days90(9)`, `Days180(10)`, `Days365(11)` |\n| `bsrMin`   | int?        | 1         | ≥ 1                                                                         |\n| `bsrMax`   | int?        | 100000    | ≥ 1, `bsrMin ≤ bsrMax`                                                      |\n| `bsrYear`  | short?      | null      | 2000–(current year + 1). Required together with `bsrMonth`. Use only with `bsrType=Historical`. |\n| `bsrMonth` | short?      | null      | 1–12. Required together with `bsrYear`.                                     |\n\n### Product filters\n\n| Field              | Type    | Default     | Notes                                              |\n|--------------------|---------|-------------|----------------------------------------------------|\n| `bindingType`      | enum?   | null (all)  | `All`, `Paperback`, `Hardcover`                    |\n| `publisherType`    | enum?   | `All`       | `All`, `SelfPublishersOnly`, `PublishersOnly`      |\n| `interiorType`     | enum?   | `BlackWhite`| `BlackWhite`, `FullColor`                          |\n| `vatType`          | enum?   | `Reduced`   | `Reduced`, `Standard`                              |\n| `includePreOrders` | bool?   | `false`     | —                                                  |\n\n### Quality filters\n\n| Field         | Type    | Constraint                         |\n|---------------|---------|------------------------------------|\n| `ratingMin`   | double? | 0.0–5.0, `ratingMin ≤ ratingMax`   |\n| `ratingMax`   | double? | 0.0–5.0                            |\n| `reviewsMin`  | int?    | ≥ 0, `reviewsMin ≤ reviewsMax`     |\n| `reviewsMax`  | int?    | ≥ 0                                |\n\n### Economic filters\n\n| Field                        | Type     | Notes                                                |\n|------------------------------|----------|------------------------------------------------------|\n| `royaltyMin`                 | decimal? | In currency units, **not** cents. `min ≤ max`.       |\n| `royaltyMax`                 | decimal? | —                                                    |\n| `monthsSincePublicationMin`  | int?     | `min ≤ max`                                          |\n| `monthsSincePublicationMax`  | int?     | —                                                    |\n\n### Discovery\n\n| Field             | Type     | Default | Constraint                                                  |\n|-------------------|----------|---------|-------------------------------------------------------------|\n| `includeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. **Each element is matched as a literal contiguous substring** (case-insensitive) against title, publisher and authors. Multiple elements are combined with **OR**. Pass multi-word phrases as a single element (`[\"small business taxes\"]`), NOT as separate tokens (`[\"small\",\"business\",\"taxes\"]`) — the latter would match any book containing just one of those words. |\n| `excludeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. Same matching semantics as `includeKeywords`: each element is a literal contiguous substring; books matching **any** element on title, publisher or authors are excluded. |\n| `categoryIds`     | long[]   | null    | ≤ 100 items, each > 0 (Amazon BrowseNode IDs). **Subtree-expanded**: passing a non-leaf node (e.g. depth-2 \"Quick & Easy\", `catId=17`) returns every book tagged with any descendant leaf — you do not need to enumerate leaves yourself. Pass any node from `/categories`, `/categories/search`, `/categories/children`, or `/categories/ancestors`. Mixing leaves and parents in one call is allowed (logical OR across the union of the expanded sets). Stale / unknown `catId`s collapse to no overlap and are silently dropped, not an error. |\n| `excludeFiction`  | bool?    | `true`  | When `true` (default), filters out fiction books — i.e. books whose categories all roll up to depth-2 ancestors flagged as fiction (Literature & Fiction, Romance, Mystery, Sci-Fi, Children's Books, Teen/YA, etc.) under the local Books root for the requested marketplace. A book is kept if **at least one** of its categories has a depth-2 ancestor flagged as non-fiction. Set `false` to include fiction. **Auto-disabled** when `categoryIds` is non-empty: explicit category intent overrides the fiction filter, otherwise passing a fiction category with the default would silently return zero results. |\n\n### Pagination\n\n| Field    | Type | Default | Range      |\n|----------|------|---------|------------|\n| `limit`  | int? | 100     | 1–300      |\n| `offset` | int? | 0       | 0–100000   |\n\n## Workflow\n\n1. **Marketplace** — Identify `domainId` from intent using the marketplace table. If ambiguous (e.g. \"Amazon\"), ask which country.\n2. **Translate** — Map every natural-language filter to its schema field. Don't guess enum values; use the table. Note: by default fiction is excluded (`excludeFiction=true`). If the user asks for fiction (e.g. \"romance\", \"mystery novels\", \"show me fiction too\") or a mixed catalog, set `excludeFiction: false` explicitly. Most KDP/low-content niche queries are non-fiction so the default is usually correct.\n3. **Pagination** — Default to `limit: 50`. Raise to 100–300 only if the user explicitly wants many results. Start with `offset: 0`.\n4. **Send** — POST the JSON body. Inspect the status code first.\n5. **Paginate if needed** — If `returnedCount == limit`, more results likely exist. Offer to fetch the next page with `offset += limit`.\n6. **Present** — Surface `title`, `asin`, `bsr`, sales/revenue estimates, and a clickable cover URL (see response notes).\n\n## Example request\n\n```json\n{\n  \"domainId\": 4,\n  \"bsrType\": \"Days90\",\n  \"bsrMin\": 1,\n  \"bsrMax\": 50000,\n  \"bindingType\": \"Paperback\",\n  \"publisherType\": \"SelfPublishersOnly\",\n  \"ratingMin\": 4.0,\n  \"reviewsMin\": 10,\n  \"interiorType\": \"BlackWhite\",\n  \"vatType\": \"Reduced\",\n  \"royaltyMin\": 2.50,\n  \"royaltyMax\": 15.00,\n  \"monthsSincePublicationMax\": 24,\n  \"includePreOrders\": false,\n  \"includeKeywords\": [\"journal\", \"notebook\"],\n  \"excludeKeywords\": [\"coloring\"],\n  \"categoryIds\": [266162, 3248921],\n  \"excludeFiction\": false,\n  \"limit\": 50,\n  \"offset\": 0\n}\n```\n\n## Example cURL\n\n```bash\ncurl -X POST \"https://api.beyondbsr.com/api/v1/books/search\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -d '{\n    \"domainId\": 4,\n    \"bsrType\": \"Days90\",\n    \"bsrMax\": 50000,\n    \"bindingType\": \"Paperback\",\n    \"publisherType\": \"SelfPublishersOnly\",\n    \"ratingMin\": 4.0,\n    \"limit\": 50\n  }'\n```\n\n## Response schema\n\n### Envelope — `BookSearchApiResponse`\n\n| Field           | Type    | Notes                                                              |\n|-----------------|---------|--------------------------------------------------------------------|\n| `returnedCount` | int     | Items in **this page**. NOT a total-match count (no total exposed).|\n| `limit`         | int     | Effective limit applied (capped at 300).                           |\n| `offset`        | int     | Effective offset applied.                                          |\n| `results`       | array   | `BookSearchResultDto[]`. Empty if no match.                        |\n\n### Result item — `BookSearchResultDto` (most relevant fields)\n\n| Field                | Type     | Notes                                                                                  |\n|----------------------|----------|----------------------------------------------------------------------------------------|\n| `id`                 | long     | Internal book ID.                                                                      |\n| `asin`               | string   | 10-char Amazon ASIN.                                                                   |\n| `title`              | string   | Full title.                                                                            |\n| `authors`            | string?  | Comma-separated, ordered by `display_order`.                                           |\n| `publicationDate`    | datetime?| Nullable.                                                                              |\n| `publisher`          | string?  | Manufacturer/publisher name.                                                           |\n| `coverImageFilename` | string?  | Build URL: `https://m.media-amazon.com/images/I/{filename}`.                           |\n| `imageFilenames`     | string[] | All carousel images (same URL pattern).                                                |\n| `rating`             | double   | 0.0–5.0.                                                                               |\n| `reviews`            | int      | Total reviews.                                                                         |\n| `bsr`                | int      | **The column matching the requested `bsrType`** — i.e. the value filtered/sorted on.   |\n| `bsrCurrent`         | int?     | Latest snapshot BSR, regardless of `bsrType`.                                          |\n| `avgBsr7d`           | int?     | Always populated.                                                                      |\n| `avgBsr30d`          | int?     | Always populated.                                                                      |\n| `avgBsr90d`          | int?     | Always populated.                                                                      |\n| `avgBsr180d`         | int?     | Always populated.                                                                      |\n| `avgBsr365d`         | int?     | Always populated.                                                                      |\n| `pageCount`          | int?     | —                                                                                      |\n| `priceCents`         | int?     | List price (MSRP) in cents.                                                            |\n| `dailyEstimate`      | decimal  | Estimated copies/day from BSR model.                                                   |\n| `weeklyEstimate`     | decimal  | Estimated copies/week.                                                                 |\n| `monthlyEstimate`    | decimal  | Estimated copies/month.                                                                |\n| `quarterlyEstimate`  | decimal  | Copies/quarter (needs ≥ 13 weeks of data).                                             |\n| `royalty*Cents`      | int?     | Per-copy royalty in cents. 4 combinations: B/W or Color × Reduced or Standard VAT.     |\n| `*Revenue*Cents`     | long?    | Derived = estimate × royalty. 16 fields total: {daily,weekly,monthly,quarterly} × {Black,Color} × {Reduced,Standard}. |\n| `binding`            | string?  | Localised label (e.g. `\"Paperback\"`, `\"Hardcover\"`, `\"Non-standard\"`).                 |\n| `dimensions`         | string?  | Formatted, prefixed by binding. Example: `\"Paperback: 152 x 8 x 229 mm\"`.              |\n| `trim/spineWidthMm`  | int?     | Trim/spine measurements.                                                               |\n| `categories`         | long[]   | Raw leaf Amazon `cat_id`s the book is tagged with (verbatim from the database). Preserves original tag order. May be empty for books still being enriched. |\n| `categoryPaths`      | `Array<Array<{catId:long,name:string,depth:int}>>` | One inner array per entry in `categories`, each is the full root → leaf ancestor chain sorted by `depth` ascending. Use this to aggregate / group books by macro or sub category in a single round-trip without calling `/categories/ancestors` per leaf. |\n\n**Important caveats**\n\n- All royalty and revenue fields are in **cents** — divide by 100 before showing currency.\n- `bsr` ≠ `bsrCurrent`. `bsr` is whatever column was chosen by `bsrType`; `bsrCurrent` is always the latest snapshot.\n- In `bsrType=Historical` mode, the `avgBsrXd` averages come from the current snapshot table while `bsr` itself is the historical month value — there is a documented temporal asymmetry inside the same response. Mention this if the user is doing a strict historical analysis.\n- `categoryPaths.length === categories.length` for a healthy dataset. If a leaf `cat_id` has been deleted upstream after the book was indexed, the corresponding inner array is silently dropped (never null) — so `categoryPaths.length` may be ≤ `categories.length`. The `categories` array always reflects the original tag set.\n- Both `categories` and `categoryPaths` may be **empty arrays `[]`** (never `null`) for books still being enriched or whose taxonomy snapshot hasn't propagated yet. Aggregation code must skip these books rather than fail on missing chains.\n\n### Aggregating results by category\n\nEach `categoryPaths` inner array is sorted by `depth` ascending. The taxonomy under the Books root is consistent across marketplaces (only the labels are localised):\n\n| Depth | Role | US example | IT example |\n|-------|------|------------|-----------|\n| 0 | Books root | `Books` | `Libri` |\n| 1 | Container shell | `Subjects` | `Categorie` |\n| 2 | **Top-level subject** (\"macro\") | `Self-Help`, `Cookbooks, Food & Wine`, `Crafts, Hobbies & Home` | `Cucina, casa e giardinaggio` |\n| 3 | **Sub-category** | `Crafts & Hobbies`, `Christian Books & Bibles` | — |\n| 4-5 | **Niche / micro-niche** (KDP-relevant) | `Coloring Books for Grown-Ups`, `Bible Study & Reference` | — |\n\nTo produce a market-opportunity report, group on the **`catId` at the desired depth**, not the `name` (names are locale-dependent; `catId` is stable). For KDP niche research the interesting depths are **3 and 4**: depth 2 is usually too broad (e.g. \"Crafts & Hobbies\" alone covers thousands of books), while depth 3-4 isolates real nicchie (\"Coloring Books for Grown-Ups\", \"Word Search\", \"Christian Living\").\n\nPattern — nested macro → sub-niche histogram with revenue rollup, from one search response:\n\n```js\n// macro (depth=2) -> { name, books, subniches: Map<catId, { name, books, monthlyRevCents }> }\nconst report = new Map();\n\nfor (const book of response.results) {\n  const seenMacros  = new Set();   // dedupe within the same book\n  const seenNiches  = new Set();\n\n  for (const chain of book.categoryPaths) {        // may be [] for un-enriched books\n    const macro = chain.find(n => n.depth === 2);\n    const niche = chain.find(n => n.depth === 3) ?? chain.find(n => n.depth === 4);\n    if (!macro) continue;\n\n    if (!seenMacros.has(macro.catId)) {\n      seenMacros.add(macro.catId);\n      const m = report.get(macro.catId) ?? { name: macro.name, books: 0, subniches: new Map() };\n      m.books++;\n      report.set(macro.catId, m);\n    }\n    if (niche && !seenNiches.has(niche.catId)) {\n      seenNiches.add(niche.catId);\n      const m = report.get(macro.catId);\n      const n = m.subniches.get(niche.catId) ?? { name: niche.name, books: 0, monthlyRevCents: 0 };\n      n.books++;\n      // pick whichever revenue field matches the user's interior/VAT context\n      n.monthlyRevCents += book.monthlyRevenueBlackReducedVatCents ?? 0;\n      m.subniches.set(niche.catId, n);\n    }\n  }\n}\n```\n\nSample fragment of a result with two categories (US, depth-1 is always `Subjects` — depth-2 is the macro):\n\n```json\n{\n  \"asin\": \"1234567890\",\n  \"title\": \"Quick Weeknight Dinners\",\n  \"categories\": [4259, 9876],\n  \"categoryPaths\": [\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",             \"depth\": 1 },\n      { \"catId\": 6,      \"name\": \"Cookbooks, Food & Wine\",\"depth\": 2 },\n      { \"catId\": 17,     \"name\": \"Quick & Easy\",         \"depth\": 3 },\n      { \"catId\": 4259,   \"name\": \"General\",              \"depth\": 4 }\n    ],\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                       \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",                    \"depth\": 1 },\n      { \"catId\": 10,     \"name\": \"Health, Fitness & Dieting\",   \"depth\": 2 },\n      { \"catId\": 9876,   \"name\": \"Diet & Weight Loss\",          \"depth\": 3 }\n    ]\n  ]\n}\n```\n\n**One-shot aggregation tip.** When the goal is a single histogram/report (as opposed to interactive paging), call `POST /books/search` with `limit=300` (the max). A 300-result response is ~850 KB and returns in ~200-350 ms in production — almost always cheaper than paginating. Only fall back to paged fetches if more than 300 books matter for the report (rare for niche analysis).\n\n## BSR History endpoint\n\nSingle-ASIN BSR timeline. Returns raw snapshots from the time-series store, ordered ascending by `recordedAt`.\n\n```\nGET https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### When to use\n\n- The user has a specific ASIN and wants its BSR over time (chart, drill-down, sanity-check the search-time `avgBsrXd` averages).\n- The user wants to verify a book's recent rank trajectory (e.g. \"is it gaining or losing visibility?\").\n\nIf the user has filter criteria but no specific ASIN, use `POST /search` first, then call this endpoint per ASIN of interest.\n\n### Path & query parameters\n\n| Param      | In    | Type   | Required | Constraint                                                                 |\n|------------|-------|--------|----------|----------------------------------------------------------------------------|\n| `domainId` | path  | int    | yes      | US (`1`) or FR (`4`) — see Marketplace domains table (validator allows 1–12). |\n| `asin`     | path  | string | yes      | Exactly 10 chars, regex `^[A-Z0-9]{10}$` (uppercase letters / digits only). |\n| `days`     | query | int?   | no       | 1–365. Default `365`. Window is `[now - days, now]` UTC.                   |\n\n### Example request\n\n```\nGET /api/v1/books/1/1635864348/bsr-history?days=90\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### Example cURL\n\n```bash\ncurl -X GET \"https://api.beyondbsr.com/api/v1/books/1/1635864348/bsr-history?days=90\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -H \"Accept: application/json\"\n```\n\n### Response schema — `BookBsrHistoryApiResponse`\n\n| Field         | Type        | Notes                                                              |\n|---------------|-------------|--------------------------------------------------------------------|\n| `asin`        | string      | Echoed from request.                                               |\n| `domainId`    | int         | Echoed from request.                                               |\n| `fromUtc`     | datetime    | Window start (`now - days`), UTC.                                  |\n| `toUtc`       | datetime    | Window end (`now`), UTC.                                           |\n| `pointCount`  | int         | Number of BSR snapshots in `points`.                               |\n| `points`      | array       | `BsrPointDto[]` ordered ascending by `recordedAt`.                 |\n\n`BsrPointDto`:\n\n| Field              | Type      | Notes                                                          |\n|--------------------|-----------|----------------------------------------------------------------|\n| `recordedAt`       | datetime  | UTC timestamp of the snapshot.                                 |\n| `bestSellersRank`  | int?      | BSR at that timestamp. May be null if Keepa returned no rank.  |\n\nExample body:\n\n```json\n{\n  \"asin\": \"1635864348\",\n  \"domainId\": 1,\n  \"fromUtc\": \"2026-01-27T00:00:00Z\",\n  \"toUtc\": \"2026-04-27T00:00:00Z\",\n  \"pointCount\": 412,\n  \"points\": [\n    { \"recordedAt\": \"2026-01-27T03:14:00Z\", \"bestSellersRank\": 1234 },\n    { \"recordedAt\": \"2026-01-27T15:02:00Z\", \"bestSellersRank\": 1218 }\n  ]\n}\n```\n\n### Status codes (BSR history specific)\n\n| Code | Meaning                       | Notes                                                                 |\n|------|-------------------------------|-----------------------------------------------------------------------|\n| 200  | OK                            | `pointCount` may be 0 if no snapshots exist in the window.            |\n| 400  | Validation failed             | `ValidationProblemDetails`. Common causes: `days` out of range, `asin` wrong format, `domainId` out of `[1,12]`. |\n| 404  | ASIN not found for that domain| Empty body. The book is not tracked in BeyondBSR for this marketplace. Tell the user — do not retry with the same pair. |\n| 401 / 429 / 500 | See generic table below.            |\n\n### Caveats\n\n- Granularity is **raw**: Keepa snapshots arrive 1–4× per day on actively-tracked books. A 365-day window typically yields 365–1460 points. No daily aggregation is applied.\n- The endpoint returns only `(recordedAt, bestSellersRank)`. Use `POST /search` if you also need rating/review/price data.\n- ASIN is case-sensitive — must be uppercase. `1635864348` (all digits) is valid.\n- Default 365 days is the cap. Longer histories are not exposed; do not retry with `days > 365`.\n\n## Categories endpoints\n\nThe four endpoints under `/api/v1/categories` expose the Amazon taxonomy (browse nodes) BeyondBSR has ingested per marketplace. They share the same API-key auth, rate limit (`api-key` policy), and `domainId` semantics as book search. All four are `GET`, JSON out, no request body.\n\n### Use cases\n\n- **Resolve a name → `catId`** to pass into `POST /books/search` `categoryIds`. Example: user asks for \"manga\" books on US → call `/categories/search?domainId=1&q=manga` to get candidate cat_ids, then feed the chosen ones into `categoryIds`.\n- **Top-level book categories** of a marketplace (e.g. \"list the main Books categories on Amazon.fr\"): `GET /categories?domainId=4&depth=2`.\n- **Walk down the tree** from a known node (e.g. \"what's under Cookbooks?\"): `GET /categories/children?domainId=1&catId=6`.\n- **Walk up the tree / breadcrumb** for a leaf node (e.g. \"where does cat 4142740011 sit?\"): `GET /categories/ancestors?domainId=1&catId=4142740011` → returns root → … → node, ordered by depth.\n\n### Shared response object — `CategoryBrowseNodeDto`\n\n| Field             | Type     | Notes                                                                                       |\n|-------------------|----------|---------------------------------------------------------------------------------------------|\n| `catId`           | long     | Amazon browse node ID. **This is the value to pass into `categoryIds` in book search.**     |\n| `parentCatId`     | long?    | Parent's `catId`. `null` for roots.                                                         |\n| `rootCatId`       | long     | Top-level ancestor's `catId` (e.g. the Books root for the marketplace).                     |\n| `name`            | string   | Localised name in the marketplace's language.                                               |\n| `contextFreeName` | string?  | Name without parent context (Keepa-provided). May be null.                                  |\n| `depth`           | int      | 0 = root. Top-level book categories are typically depth 2 (root → Categorie → top node).    |\n| `isFiction`       | bool     | Internal flag used by `excludeFiction` in book search. Only meaningful at depth=2 under Books root. |\n| `productCount`    | int?     | Approximate number of products in that node (Keepa-reported). May be null.                  |\n\n### 1. List categories at a depth (top-level Books browser)\n\n```\nGET /api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\n```\n\n| Param            | Type  | Default | Notes                                                                                                                                  |\n|------------------|-------|---------|----------------------------------------------------------------------------------------------------------------------------------------|\n| `domainId`       | int   | —       | Required. Marketplace ID (1-12, but only `1` US and `4` FR return data).                                                                |\n| `depth`          | int?  | 2       | Hierarchy level. 0 = root. 2 = top-level book categories (\"Self-Help\", \"Cookbooks…\", etc.). Allowed 0-5.                               |\n| `includeFiction` | bool? | `false` | Whether to include nodes flagged as fiction (Literature & Fiction, Romance, Sci-Fi, Children's Books, Teen/YA, Comics, etc.).          |\n\n**Filters applied automatically (do not appear in params):** only browse nodes (`isBrowseNode=true`), and only nodes whose root is the **Books** root of the marketplace — i.e. non-book trees (toys, electronics) are excluded. Use this endpoint to enumerate the canonical KDP-relevant taxonomy.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nResponse envelope — `CategoriesApiResponse`:\n\n| Field           | Type    | Notes                                |\n|-----------------|---------|--------------------------------------|\n| `domainId`      | int     | Echo.                                |\n| `depth`         | int     | Echo (resolved default if omitted).  |\n| `returnedCount` | int     | Number of items in `results`.        |\n| `results`       | array   | `CategoryBrowseNodeDto[]`.           |\n\n### 2. Direct children of a category\n\n```\nGET /api/v1/categories/children?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                                            |\n|------------|------|----------------------------------------------------------------------------------|\n| `domainId` | int  | Required.                                                                        |\n| `catId`    | long | Required. Parent category's Amazon browse node ID.                               |\n\n**No implicit filters.** Returns ALL active direct children of the parent — including non-browse nodes and fiction nodes. Use it for true tree navigation regardless of book/non-book context.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nReturns **404** if `catId` does not exist for the given marketplace. Do not retry on 404.\n\nResponse envelope — `CategoryChildrenApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `parentCatId`   | long   | Echo of the input `catId`.           |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n### 3. Search categories by name\n\n```\nGET /api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\n```\n\n| Param      | Type    | Default | Notes                                                                                                              |\n|------------|---------|---------|--------------------------------------------------------------------------------------------------------------------|\n| `domainId` | int     | —       | Required.                                                                                                          |\n| `q`        | string  | —       | Required. Case-insensitive substring match on `name`. Min **3** chars, max 100. Wildcards (`%`, `_`, `\\`) are treated as literals (escaped server-side). |\n| `limit`    | int?    | 50      | 1-200. Pushed down to SQL.                                                                                         |\n\n**No implicit filters.** Searches across the ENTIRE category tree of the marketplace (not just Books) — so a query like \"Sports\" on Amazon.com will surface both \"Sports & Outdoors\" (under Books) and the toy/apparel \"Sports\" nodes. The agent should filter client-side by `rootCatId` if the user only wants book categories.\n\nOrder: `productCount` DESC, then `depth` ASC, then `name` ASC.\n\nResponse envelope — `CategorySearchApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `query`         | string | Echo of `q`.                         |\n| `limit`         | int    | Effective limit applied.             |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n**Tip:** if the user-typed term is broad (e.g. \"fiction\") and the default `limit=50` truncates likely candidates, raise `limit` to 200. If still not enough, refine the term or use `/categories?depth=2` instead.\n\n### 4. Ancestors / breadcrumb of a category\n\n```\nGET /api/v1/categories/ancestors?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                  |\n|------------|------|--------------------------------------------------------|\n| `domainId` | int  | Required.                                              |\n| `catId`    | long | Required. Category whose breadcrumb to retrieve.       |\n\nReturns the full ancestor chain **including the node itself**, ordered by `depth` ASC. No implicit filters (intermediate non-browse / promotional nodes are included).\n\nReturns **404** if `catId` does not exist for the marketplace.\n\nResponse envelope — `CategoryAncestorsApiResponse`:\n\n| Field           | Type   | Notes                                                                |\n|-----------------|--------|----------------------------------------------------------------------|\n| `domainId`      | int    | Echo.                                                                |\n| `catId`         | long   | Echo.                                                                |\n| `returnedCount` | int    | Length of the chain (includes the requested node).                   |\n| `results`       | array  | `CategoryBrowseNodeDto[]` from root (depth=0) to the node itself.    |\n\n### Example workflows\n\n**Resolve \"manga\" on Amazon.com then search books** (replace `<catId>` with a candidate returned by step 1):\n\n```bash\n# 1. find candidate cat_ids\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/search?domainId=1&q=manga&limit=10\"\n\n# 2. inspect the breadcrumb of a candidate to confirm it's under Books\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/ancestors?domainId=1&catId=<catId>\"\n\n# 3. drill down children if needed\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories/children?domainId=1&catId=<catId>\"\n\n# 4. plug the chosen cat_id(s) into book search\ncurl -X POST -H \"X-API-Key: $BOOKSEARCH_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"domainId\":1,\"categoryIds\":[<catId>],\"limit\":50}' \\\n  \"https://api.beyondbsr.com/api/v1/books/search\"\n```\n\n**List top-level Books categories on Amazon.com (non-fiction only, default):**\n\n```bash\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://api.beyondbsr.com/api/v1/categories?domainId=1&depth=2\"\n```\n\n### Status codes (categories endpoints)\n\n| Code | Meaning                          | Notes                                                                                       |\n|------|----------------------------------|---------------------------------------------------------------------------------------------|\n| 200  | OK                               | `results` may be empty (e.g. no children, no matches, marketplace not seeded).              |\n| 400  | Validation failed                | `ValidationProblemDetails`. Common causes: `domainId` out of range, `q` < 3 chars, `limit` out of 1-200, `depth` out of 0-5. |\n| 401  | Auth failed                      | Same handling as book search — stop, report misconfigured key.                              |\n| 404  | `catId` not found for `domainId` | Empty body. Only on `/children` and `/ancestors`. Do not retry the same pair.               |\n| 429  | Rate limit                       | Honour `Retry-After`. Same key budget as book search (30 req/min).                          |\n| 500  | Server error                     | Retry once, then escalate.                                                                  |\n\n### Caveats\n\n- **Read-only.** No POST/PUT/DELETE on categories.\n- **`catId` vs internal id.** The wire contract exposes only Amazon's `catId` (browse node ID). The internal database `id` is never surfaced and is not interchangeable.\n- **`depth=2` ≠ \"top-level under Books\" universally.** Most marketplaces seed Books root at depth=0 → \"Categorie/Categories\" at depth=1 → top nodes at depth=2. If `/categories?depth=2` returns unexpectedly few rows for a marketplace, retry with `depth=1`.\n- **`excludeFiction` semantics propagate.** A node's `isFiction=true` here is exactly what `excludeFiction` in book search rolls up on. If a user complains that a fiction sub-genre isn't being excluded, the agent can spot-check via `/categories/ancestors?catId=…` whether the depth-2 ancestor is flagged.\n- **Children endpoint includes non-browse nodes.** Some Amazon nodes are promotional or grouping shells (`isBrowseNode=false`). They cannot be used as `categoryIds` filters in book search. Skip them or warn the user.\n\n## Status codes & error handling\n\n| Code | Meaning                | Body                                       | Agent action                                                                  |\n|------|------------------------|--------------------------------------------|-------------------------------------------------------------------------------|\n| 200  | OK (may be empty list) | `BookSearchApiResponse`                    | Parse `results`. Empty array = no matches, not an error.                      |\n| 400  | Validation failed      | `ValidationProblemDetails` (RFC 7807)      | Read the `errors` map, fix the body, do not retry blindly. Surface the issue. |\n| 401  | Auth failed            | empty + `WWW-Authenticate: ApiKey realm=\"BeyondBSR\"` | Stop. Report misconfigured `BOOKSEARCH_API_KEY`. Do not retry.       |\n| 429  | Rate limit exceeded    | `\"Rate limit exceeded. Please try again later.\"` + `Retry-After` header | Honour `Retry-After`. Back off. Do not hammer the endpoint. |\n| 500  | Unhandled server error | `ProblemDetails` JSON                      | Retry once after a few seconds. If it persists, escalate to the user.         |\n\n### Example 400 body\n\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.1\",\n  \"title\": \"One or more validation errors occurred.\",\n  \"status\": 400,\n  \"errors\": {\n    \"DomainId\": [\"DomainId must be between 1 and 12.\"],\n    \"Limit\": [\"Limit must be between 1 and 300.\"]\n  }\n}\n```\n\n## Sorting & pagination notes\n\n- Default sort: `ORDER BY <chosen BSR column> ASC` (lower BSR = better seller appears first).\n- Historical mode (`bsrType=Historical` + `bsrYear`/`bsrMonth`): sorted by the historical monthly BSR rank ASC.\n- Tie-breaker is non-deterministic — books with identical BSR may appear in different orders across page fetches. Warn the user if they need a perfectly stable ordering.\n- No `orderBy` parameter is exposed.\n\n## Limits\n\n- **Rate limit**: 30 requests/minute per API key. Shared across all callers using the same key.\n- **Body size**: 128 KB max.\n- **Page size**: 300 results max per request (`limit ≤ 300`).\n- **Offset cap**: 100,000 (deep pagination beyond this is not supported — refine filters instead).\n\nFile v1.0.8:_meta.json\n\n{\n  \"ownerId\": \"kn71bkxxw77zyvcjdxzqpwcs658570bb\",\n  \"slug\": \"booksearch-api\",\n  \"version\": \"1.0.8\",\n  \"publishedAt\": 1780322220217\n}\n\nFile v1.0.8:skill-card.md\n\n## Description: <br>\nSearches the BeyondBSR BookSearch API for Amazon KDP book research, BSR history, and Amazon category taxonomy lookup across the supported US and France marketplaces. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ramius88](https://clawhub.ai/user/ramius88) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nAuthors, publishers, and KDP researchers use this skill to find books by marketplace, BSR, category, keyword, rating, review count, publication age, binding type, and royalty filters. It also supports single-ASIN BSR history lookup and category ID discovery for book-search filters. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires a private-beta BeyondBSR API key. <br>\nMitigation: Store the key only in the BOOKSEARCH_API_KEY environment variable, treat it as a secret, and do not print, log, or paste it into other tools. <br>\nRisk: Book research queries are sent to BeyondBSR. <br>\nMitigation: Confirm the user is comfortable sending query terms and filters to BeyondBSR before using the API. <br>\nRisk: The documented data coverage is limited to US and France marketplaces. <br>\nMitigation: Use only domainId 1 for Amazon.com and domainId 4 for Amazon.fr unless future release evidence states additional marketplace data is available. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ramius88/booksearch-api) <br>\n- [Publisher profile](https://clawhub.ai/user/ramius88) <br>\n- [BeyondBSR book search endpoint](https://api.beyondbsr.com/api/v1/books/search) <br>\n- [BeyondBSR category search endpoint](https://api.beyondbsr.com/api/v1/categories/search?domainId={domainId}&q={query}&limit={limit}) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, API calls, shell commands, configuration, guidance] <br>\n**Output Format:** [Markdown summaries with JSON request examples and optional shell commands] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires BOOKSEARCH_API_KEY and returns read-oriented book, BSR history, and category taxonomy results from BeyondBSR.] <br>\n\n## Skill Version(s): <br>\n1.0.8 (source: server release metadata) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nArchive v1.0.7: 3 files, 14819 bytes\n\nFiles: skill-card.md (2544b), SKILL.md (41783b), _meta.json (133b)\n\nFile v1.0.7:SKILL.md\n\n---\nname: booksearch-api\ndescription: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (browse nodes) for any supported marketplace. Each book-search result now ships its full root → leaf category ancestor chain(s) inline, so the skill can aggregate market-opportunity reports by macro/sub category without follow-up calls. Use this skill whenever the user wants to discover, filter, or research self-published or traditionally published books on Amazon by BSR, category, keyword, royalty range, rating, reviews, publication date, binding type, or marketplace (US, UK, DE, FR, IT, ES, CA); when the user wants the BSR timeline of a specific ASIN over the last N days; when the user wants to look up Amazon category codes (browse node IDs), walk the category tree (children, ancestors/breadcrumb), or search categories by name to use as filters in book search; or when the user wants to group/aggregate search results by macro category (e.g. \"how many Personal Finance opportunities, broken down by sub-category?\"). Typical intents include KDP niche research, low-competition book discovery, sales estimation, royalty/revenue projection, competitor analysis, paperback/hardcover filtering, bulk listing of books matching numeric/textual criteria, single-ASIN BSR history charts, resolving a human-readable category name (e.g. \"manga\", \"self-help\") into the Amazon `catId` to pass to `categoryIds` in book search, and producing category-grouped market-opportunity summaries from a single search response. Do not use for price-history timelines, review/rating timelines, or account/user data — only book search, BSR history, and category browsing are exposed.\nmetadata:\n  clawdbot:\n    requires:\n      env:\n        - BOOKSEARCH_API_KEY\n---\n\n# BookSearch API\n\n## ⚠️ API Access & Beta Program\n\nThe BeyondBSR BookSearch API is currently in **private beta**. This skill requires an API key (`BOOKSEARCH_API_KEY`) which is **not publicly available** at this time.\n\nUsers interested in accessing Amazon KDP book data (BSR history, reviews, categories, keyword research) can apply to the early adopter program by contacting **support@beyondbsr.com**. Requests are reviewed individually and approved keys are issued on a case-by-case basis.\n\nWithout a valid key, all endpoints below will return `401 Unauthorized`.\n\n---\n\nProgrammatic search over the BeyondBSR book catalogue, BSR history retrieval for a single book, and Amazon category taxonomy browsing. Six endpoints, JSON in / JSON out, API-key auth.\n\n## When to use this skill\n\nUse it when the user asks to:\n\n- Find books matching numeric filters: BSR range, rating, reviews count, royalty, page count age, publication recency.\n- Discover niches by keyword inclusion/exclusion or Amazon category IDs.\n- Filter by marketplace (Amazon.com, .co.uk, .de, .fr, .it, .es, .ca).\n- Distinguish self-publishers from traditional publishers.\n- Estimate sales (daily / weekly / monthly / quarterly) and revenue per copy.\n- Compare BSR averages across multiple time windows (7d / 30d / 90d / 180d / 365d).\n- Retrieve the **BSR timeline** of a single book (by `domainId` + `asin`) over the last N days, e.g. for charting rank evolution.\n- **Resolve a category name into an Amazon `catId`** (browse node ID), look up a category's direct children, walk its breadcrumb up to the root, or list top-level book categories for a marketplace — to feed `categoryIds` into book search, or just to explore the taxonomy.\n\n**Do NOT use** for: price-history charts, review/rating timelines, account/user data, or anything not in the response schemas below. Those are out of scope.\n\n## Endpoints\n\n```\nPOST https://beyondbsr.com/api/v1/books/search\nGET  https://beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nGET  https://beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\nGET  https://beyondbsr.com/api/v1/categories/children?domainId={..}&catId={..}\nGET  https://beyondbsr.com/api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\nGET  https://beyondbsr.com/api/v1/categories/ancestors?domainId={..}&catId={..}\nContent-Type: application/json   (book search only)\nX-API-Key: $BOOKSEARCH_API_KEY\n```\n\n## Authentication\n\n- Read the key from the `BOOKSEARCH_API_KEY` env var. Format: `bbsr_live_<43-char-base64url>`.\n- **Never** print, echo, log, or include the key in any user-facing output. Never paste it into another tool's input.\n- On `401 Unauthorized`: do not retry. Report \"API key missing or invalid — check `BOOKSEARCH_API_KEY` env var\" and stop.\n- Send `X-API-Key` exactly once. Multi-valued headers are rejected.\n\n## Marketplace domains\n\nMap natural-language marketplace references (e.g. \"Amazon.de\", \"the UK store\", \"amazon italia\") to `domainId` using this table:\n\n| domainId | locale | country | name           |\n|----------|--------|---------|----------------|\n| 1        | com    | US      | United States  |\n| 2        | co.uk  | GB      | United Kingdom |\n| 3        | de     | DE      | Germany        |\n| 4        | fr     | FR      | France         |\n| 6        | ca     | CA      | Canada         |\n| 8        | it     | IT      | Italy          |\n| 9        | es     | ES      | Spain          |\n\nIDs `5` and `7` are intentional gaps in the dataset — do not invent them. The validator technically accepts `1–12`, but only the seven IDs above are guaranteed to return data. If the user asks for a marketplace not listed (e.g. Japan, Australia), tell them it is not currently supported.\n\n## Request schema\n\nAll fields are optional except `domainId`. Enums accept either the string name (e.g. `\"Weekly\"`) or the integer value.\n\n### Required\n\n| Field      | Type | Constraint                  |\n|------------|------|-----------------------------|\n| `domainId` | int  | One of the 7 IDs in the marketplace table above (validator allows 1–12). |\n\n### BSR filters\n\n| Field      | Type        | Default   | Notes                                                                       |\n|------------|-------------|-----------|-----------------------------------------------------------------------------|\n| `bsrType`  | enum        | `Weekly`  | `Historical(-1)`, `Current(0)`, `Weekly(7)`, `Days30(8)`, `Days90(9)`, `Days180(10)`, `Days365(11)` |\n| `bsrMin`   | int?        | 1         | ≥ 1                                                                         |\n| `bsrMax`   | int?        | 100000    | ≥ 1, `bsrMin ≤ bsrMax`                                                      |\n| `bsrYear`  | short?      | null      | 2000–(current year + 1). Required together with `bsrMonth`. Use only with `bsrType=Historical`. |\n| `bsrMonth` | short?      | null      | 1–12. Required together with `bsrYear`.                                     |\n\n### Product filters\n\n| Field              | Type    | Default     | Notes                                              |\n|--------------------|---------|-------------|----------------------------------------------------|\n| `bindingType`      | enum?   | null (all)  | `All`, `Paperback`, `Hardcover`                    |\n| `publisherType`    | enum?   | `All`       | `All`, `SelfPublishersOnly`, `PublishersOnly`      |\n| `interiorType`     | enum?   | `BlackWhite`| `BlackWhite`, `FullColor`                          |\n| `vatType`          | enum?   | `Reduced`   | `Reduced`, `Standard`                              |\n| `includePreOrders` | bool?   | `false`     | —                                                  |\n\n### Quality filters\n\n| Field         | Type    | Constraint                         |\n|---------------|---------|------------------------------------|\n| `ratingMin`   | double? | 0.0–5.0, `ratingMin ≤ ratingMax`   |\n| `ratingMax`   | double? | 0.0–5.0                            |\n| `reviewsMin`  | int?    | ≥ 0, `reviewsMin ≤ reviewsMax`     |\n| `reviewsMax`  | int?    | ≥ 0                                |\n\n### Economic filters\n\n| Field                        | Type     | Notes                                                |\n|------------------------------|----------|------------------------------------------------------|\n| `royaltyMin`                 | decimal? | In currency units, **not** cents. `min ≤ max`.       |\n| `royaltyMax`                 | decimal? | —                                                    |\n| `monthsSincePublicationMin`  | int?     | `min ≤ max`                                          |\n| `monthsSincePublicationMax`  | int?     | —                                                    |\n\n### Discovery\n\n| Field             | Type     | Default | Constraint                                                  |\n|-------------------|----------|---------|-------------------------------------------------------------|\n| `includeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. **Each element is matched as a literal contiguous substring** (case-insensitive) against title, publisher and authors. Multiple elements are combined with **OR**. Pass multi-word phrases as a single element (`[\"small business taxes\"]`), NOT as separate tokens (`[\"small\",\"business\",\"taxes\"]`) — the latter would match any book containing just one of those words. |\n| `excludeKeywords` | string[] | null    | ≤ 25 items, each ≤ 100 chars, non-empty. Same matching semantics as `includeKeywords`: each element is a literal contiguous substring; books matching **any** element on title, publisher or authors are excluded. |\n| `categoryIds`     | long[]   | null    | ≤ 100 items, each > 0 (Amazon BrowseNode IDs). **Subtree-expanded**: passing a non-leaf node (e.g. depth-2 \"Quick & Easy\", `catId=17`) returns every book tagged with any descendant leaf — you do not need to enumerate leaves yourself. Pass any node from `/categories`, `/categories/search`, `/categories/children`, or `/categories/ancestors`. Mixing leaves and parents in one call is allowed (logical OR across the union of the expanded sets). Stale / unknown `catId`s collapse to no overlap and are silently dropped, not an error. |\n| `excludeFiction`  | bool?    | `true`  | When `true` (default), filters out fiction books — i.e. books whose categories all roll up to depth-2 ancestors flagged as fiction (Literature & Fiction, Romance, Mystery, Sci-Fi, Children's Books, Teen/YA, etc.) under the local Books root for the requested marketplace. A book is kept if **at least one** of its categories has a depth-2 ancestor flagged as non-fiction. Set `false` to include fiction. **Auto-disabled** when `categoryIds` is non-empty: explicit category intent overrides the fiction filter, otherwise passing a fiction category with the default would silently return zero results. Currently unsupported on Amazon.ca (`domainId=6`): with `excludeFiction=true` no books are returned for CA — pass `false` for that marketplace. |\n\n### Pagination\n\n| Field    | Type | Default | Range      |\n|----------|------|---------|------------|\n| `limit`  | int? | 100     | 1–300      |\n| `offset` | int? | 0       | 0–100000   |\n\n## Workflow\n\n1. **Marketplace** — Identify `domainId` from intent using the marketplace table. If ambiguous (e.g. \"Amazon\"), ask which country.\n2. **Translate** — Map every natural-language filter to its schema field. Don't guess enum values; use the table. Note: by default fiction is excluded (`excludeFiction=true`). If the user asks for fiction (e.g. \"romance\", \"mystery novels\", \"show me fiction too\") or a mixed catalog, set `excludeFiction: false` explicitly. Most KDP/low-content niche queries are non-fiction so the default is usually correct.\n3. **Pagination** — Default to `limit: 50`. Raise to 100–300 only if the user explicitly wants many results. Start with `offset: 0`.\n4. **Send** — POST the JSON body. Inspect the status code first.\n5. **Paginate if needed** — If `returnedCount == limit`, more results likely exist. Offer to fetch the next page with `offset += limit`.\n6. **Present** — Surface `title`, `asin`, `bsr`, sales/revenue estimates, and a clickable cover URL (see response notes).\n\n## Example request\n\n```json\n{\n  \"domainId\": 3,\n  \"bsrType\": \"Days90\",\n  \"bsrMin\": 1,\n  \"bsrMax\": 50000,\n  \"bindingType\": \"Paperback\",\n  \"publisherType\": \"SelfPublishersOnly\",\n  \"ratingMin\": 4.0,\n  \"reviewsMin\": 10,\n  \"interiorType\": \"BlackWhite\",\n  \"vatType\": \"Reduced\",\n  \"royaltyMin\": 2.50,\n  \"royaltyMax\": 15.00,\n  \"monthsSincePublicationMax\": 24,\n  \"includePreOrders\": false,\n  \"includeKeywords\": [\"journal\", \"notebook\"],\n  \"excludeKeywords\": [\"coloring\"],\n  \"categoryIds\": [266162, 3248921],\n  \"excludeFiction\": false,\n  \"limit\": 50,\n  \"offset\": 0\n}\n```\n\n## Example cURL\n\n```bash\ncurl -X POST \"https://beyondbsr.com/api/v1/books/search\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -d '{\n    \"domainId\": 3,\n    \"bsrType\": \"Days90\",\n    \"bsrMax\": 50000,\n    \"bindingType\": \"Paperback\",\n    \"publisherType\": \"SelfPublishersOnly\",\n    \"ratingMin\": 4.0,\n    \"limit\": 50\n  }'\n```\n\n## Response schema\n\n### Envelope — `BookSearchApiResponse`\n\n| Field           | Type    | Notes                                                              |\n|-----------------|---------|--------------------------------------------------------------------|\n| `returnedCount` | int     | Items in **this page**. NOT a total-match count (no total exposed).|\n| `limit`         | int     | Effective limit applied (capped at 300).                           |\n| `offset`        | int     | Effective offset applied.                                          |\n| `results`       | array   | `BookSearchResultDto[]`. Empty if no match.                        |\n\n### Result item — `BookSearchResultDto` (most relevant fields)\n\n| Field                | Type     | Notes                                                                                  |\n|----------------------|----------|----------------------------------------------------------------------------------------|\n| `id`                 | long     | Internal book ID.                                                                      |\n| `asin`               | string   | 10-char Amazon ASIN.                                                                   |\n| `title`              | string   | Full title.                                                                            |\n| `authors`            | string?  | Comma-separated, ordered by `display_order`.                                           |\n| `publicationDate`    | datetime?| Nullable.                                                                              |\n| `publisher`          | string?  | Manufacturer/publisher name.                                                           |\n| `coverImageFilename` | string?  | Build URL: `https://m.media-amazon.com/images/I/{filename}`.                           |\n| `imageFilenames`     | string[] | All carousel images (same URL pattern).                                                |\n| `rating`             | double   | 0.0–5.0.                                                                               |\n| `reviews`            | int      | Total reviews.                                                                         |\n| `bsr`                | int      | **The column matching the requested `bsrType`** — i.e. the value filtered/sorted on.   |\n| `bsrCurrent`         | int?     | Latest snapshot BSR, regardless of `bsrType`.                                          |\n| `avgBsr7d`           | int?     | Always populated.                                                                      |\n| `avgBsr30d`          | int?     | Always populated.                                                                      |\n| `avgBsr90d`          | int?     | Always populated.                                                                      |\n| `avgBsr180d`         | int?     | Always populated.                                                                      |\n| `avgBsr365d`         | int?     | Always populated.                                                                      |\n| `pageCount`          | int?     | —                                                                                      |\n| `priceCents`         | int?     | List price (MSRP) in cents.                                                            |\n| `dailyEstimate`      | decimal  | Estimated copies/day from BSR model.                                                   |\n| `weeklyEstimate`     | decimal  | Estimated copies/week.                                                                 |\n| `monthlyEstimate`    | decimal  | Estimated copies/month.                                                                |\n| `quarterlyEstimate`  | decimal  | Copies/quarter (needs ≥ 13 weeks of data).                                             |\n| `royalty*Cents`      | int?     | Per-copy royalty in cents. 4 combinations: B/W or Color × Reduced or Standard VAT.     |\n| `*Revenue*Cents`     | long?    | Derived = estimate × royalty. 16 fields total: {daily,weekly,monthly,quarterly} × {Black,Color} × {Reduced,Standard}. |\n| `binding`            | string?  | Localised label (e.g. `\"Paperback\"`, `\"Hardcover\"`, `\"Non-standard\"`).                 |\n| `dimensions`         | string?  | Formatted, prefixed by binding. Example: `\"Paperback: 152 x 8 x 229 mm\"`.              |\n| `trim/spineWidthMm`  | int?     | Trim/spine measurements.                                                               |\n| `categories`         | long[]   | Raw leaf Amazon `cat_id`s the book is tagged with (verbatim from the database). Preserves original tag order. May be empty for books still being enriched. |\n| `categoryPaths`      | `Array<Array<{catId:long,name:string,depth:int}>>` | One inner array per entry in `categories`, each is the full root → leaf ancestor chain sorted by `depth` ascending. Use this to aggregate / group books by macro or sub category in a single round-trip without calling `/categories/ancestors` per leaf. |\n\n**Important caveats**\n\n- All royalty and revenue fields are in **cents** — divide by 100 before showing currency.\n- `bsr` ≠ `bsrCurrent`. `bsr` is whatever column was chosen by `bsrType`; `bsrCurrent` is always the latest snapshot.\n- In `bsrType=Historical` mode, the `avgBsrXd` averages come from the current snapshot table while `bsr` itself is the historical month value — there is a documented temporal asymmetry inside the same response. Mention this if the user is doing a strict historical analysis.\n- `categoryPaths.length === categories.length` for a healthy dataset. If a leaf `cat_id` has been deleted upstream after the book was indexed, the corresponding inner array is silently dropped (never null) — so `categoryPaths.length` may be ≤ `categories.length`. The `categories` array always reflects the original tag set.\n- Both `categories` and `categoryPaths` may be **empty arrays `[]`** (never `null`) for books still being enriched or whose taxonomy snapshot hasn't propagated yet. Aggregation code must skip these books rather than fail on missing chains.\n\n### Aggregating results by category\n\nEach `categoryPaths` inner array is sorted by `depth` ascending. The taxonomy under the Books root is consistent across marketplaces (only the labels are localised):\n\n| Depth | Role | US example | IT example |\n|-------|------|------------|-----------|\n| 0 | Books root | `Books` | `Libri` |\n| 1 | Container shell | `Subjects` | `Categorie` |\n| 2 | **Top-level subject** (\"macro\") | `Self-Help`, `Cookbooks, Food & Wine`, `Crafts, Hobbies & Home` | `Cucina, casa e giardinaggio` |\n| 3 | **Sub-category** | `Crafts & Hobbies`, `Christian Books & Bibles` | — |\n| 4-5 | **Niche / micro-niche** (KDP-relevant) | `Coloring Books for Grown-Ups`, `Bible Study & Reference` | — |\n\nTo produce a market-opportunity report, group on the **`catId` at the desired depth**, not the `name` (names are locale-dependent; `catId` is stable). For KDP niche research the interesting depths are **3 and 4**: depth 2 is usually too broad (e.g. \"Crafts & Hobbies\" alone covers thousands of books), while depth 3-4 isolates real nicchie (\"Coloring Books for Grown-Ups\", \"Word Search\", \"Christian Living\").\n\nPattern — nested macro → sub-niche histogram with revenue rollup, from one search response:\n\n```js\n// macro (depth=2) -> { name, books, subniches: Map<catId, { name, books, monthlyRevCents }> }\nconst report = new Map();\n\nfor (const book of response.results) {\n  const seenMacros  = new Set();   // dedupe within the same book\n  const seenNiches  = new Set();\n\n  for (const chain of book.categoryPaths) {        // may be [] for un-enriched books\n    const macro = chain.find(n => n.depth === 2);\n    const niche = chain.find(n => n.depth === 3) ?? chain.find(n => n.depth === 4);\n    if (!macro) continue;\n\n    if (!seenMacros.has(macro.catId)) {\n      seenMacros.add(macro.catId);\n      const m = report.get(macro.catId) ?? { name: macro.name, books: 0, subniches: new Map() };\n      m.books++;\n      report.set(macro.catId, m);\n    }\n    if (niche && !seenNiches.has(niche.catId)) {\n      seenNiches.add(niche.catId);\n      const m = report.get(macro.catId);\n      const n = m.subniches.get(niche.catId) ?? { name: niche.name, books: 0, monthlyRevCents: 0 };\n      n.books++;\n      // pick whichever revenue field matches the user's interior/VAT context\n      n.monthlyRevCents += book.monthlyRevenueBlackReducedVatCents ?? 0;\n      m.subniches.set(niche.catId, n);\n    }\n  }\n}\n```\n\nSample fragment of a result with two categories (US, depth-1 is always `Subjects` — depth-2 is the macro):\n\n```json\n{\n  \"asin\": \"1234567890\",\n  \"title\": \"Quick Weeknight Dinners\",\n  \"categories\": [4259, 9876],\n  \"categoryPaths\": [\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",             \"depth\": 1 },\n      { \"catId\": 6,      \"name\": \"Cookbooks, Food & Wine\",\"depth\": 2 },\n      { \"catId\": 17,     \"name\": \"Quick & Easy\",         \"depth\": 3 },\n      { \"catId\": 4259,   \"name\": \"General\",              \"depth\": 4 }\n    ],\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                       \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",                    \"depth\": 1 },\n      { \"catId\": 10,     \"name\": \"Health, Fitness & Dieting\",   \"depth\": 2 },\n      { \"catId\": 9876,   \"name\": \"Diet & Weight Loss\",          \"depth\": 3 }\n    ]\n  ]\n}\n```\n\n**One-shot aggregation tip.** When the goal is a single histogram/report (as opposed to interactive paging), call `POST /books/search` with `limit=300` (the max). A 300-result response is ~850 KB and returns in ~200-350 ms in production — almost always cheaper than paginating. Only fall back to paged fetches if more than 300 books matter for the report (rare for niche analysis).\n\n## BSR History endpoint\n\nSingle-ASIN BSR timeline. Returns raw snapshots from the time-series store, ordered ascending by `recordedAt`.\n\n```\nGET https://beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### When to use\n\n- The user has a specific ASIN and wants its BSR over time (chart, drill-down, sanity-check the search-time `avgBsrXd` averages).\n- The user wants to verify a book's recent rank trajectory (e.g. \"is it gaining or losing visibility?\").\n\nIf the user has filter criteria but no specific ASIN, use `POST /search` first, then call this endpoint per ASIN of interest.\n\n### Path & query parameters\n\n| Param      | In    | Type   | Required | Constraint                                                                 |\n|------------|-------|--------|----------|----------------------------------------------------------------------------|\n| `domainId` | path  | int    | yes      | One of the 7 marketplace IDs (see Marketplace domains table; validator allows 1–12). |\n| `asin`     | path  | string | yes      | Exactly 10 chars, regex `^[A-Z0-9]{10}$` (uppercase letters / digits only). |\n| `days`     | query | int?   | no       | 1–365. Default `365`. Window is `[now - days, now]` UTC.                   |\n\n### Example request\n\n```\nGET /api/v1/books/1/1635864348/bsr-history?days=90\nX-API-Key: $BOOKSEARCH_API_KEY\nAccept: application/json\n```\n\n### Example cURL\n\n```bash\ncurl -X GET \"https://beyondbsr.com/api/v1/books/1/1635864348/bsr-history?days=90\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -H \"Accept: application/json\"\n```\n\n### Response schema — `BookBsrHistoryApiResponse`\n\n| Field         | Type        | Notes                                                              |\n|---------------|-------------|--------------------------------------------------------------------|\n| `asin`        | string      | Echoed from request.                                               |\n| `domainId`    | int         | Echoed from request.                                               |\n| `fromUtc`     | datetime    | Window start (`now - days`), UTC.                                  |\n| `toUtc`       | datetime    | Window end (`now`), UTC.                                           |\n| `pointCount`  | int         | Number of BSR snapshots in `points`.                               |\n| `points`      | array       | `BsrPointDto[]` ordered ascending by `recordedAt`.                 |\n\n`BsrPointDto`:\n\n| Field              | Type      | Notes                                                          |\n|--------------------|-----------|----------------------------------------------------------------|\n| `recordedAt`       | datetime  | UTC timestamp of the snapshot.                                 |\n| `bestSellersRank`  | int?      | BSR at that timestamp. May be null if Keepa returned no rank.  |\n\nExample body:\n\n```json\n{\n  \"asin\": \"1635864348\",\n  \"domainId\": 1,\n  \"fromUtc\": \"2026-01-27T00:00:00Z\",\n  \"toUtc\": \"2026-04-27T00:00:00Z\",\n  \"pointCount\": 412,\n  \"points\": [\n    { \"recordedAt\": \"2026-01-27T03:14:00Z\", \"bestSellersRank\": 1234 },\n    { \"recordedAt\": \"2026-01-27T15:02:00Z\", \"bestSellersRank\": 1218 }\n  ]\n}\n```\n\n### Status codes (BSR history specific)\n\n| Code | Meaning                       | Notes                                                                 |\n|------|-------------------------------|-----------------------------------------------------------------------|\n| 200  | OK                            | `pointCount` may be 0 if no snapshots exist in the window.            |\n| 400  | Validation failed             | `ValidationProblemDetails`. Common causes: `days` out of range, `asin` wrong format, `domainId` out of `[1,12]`. |\n| 404  | ASIN not found for that domain| Empty body. The book is not tracked in BeyondBSR for this marketplace. Tell the user — do not retry with the same pair. |\n| 401 / 429 / 500 | See generic table below.            |\n\n### Caveats\n\n- Granularity is **raw**: Keepa snapshots arrive 1–4× per day on actively-tracked books. A 365-day window typically yields 365–1460 points. No daily aggregation is applied.\n- The endpoint returns only `(recordedAt, bestSellersRank)`. Use `POST /search` if you also need rating/review/price data.\n- ASIN is case-sensitive — must be uppercase. `1635864348` (all digits) is valid.\n- Default 365 days is the cap. Longer histories are not exposed; do not retry with `days > 365`.\n\n## Categories endpoints\n\nThe four endpoints under `/api/v1/categories` expose the Amazon taxonomy (browse nodes) BeyondBSR has ingested per marketplace. They share the same API-key auth, rate limit (`api-key` policy), and `domainId` semantics as book search. All four are `GET`, JSON out, no request body.\n\n### Use cases\n\n- **Resolve a name → `catId`** to pass into `POST /books/search` `categoryIds`. Example: user asks for \"manga\" books on US → call `/categories/search?domainId=1&q=manga` to get candidate cat_ids, then feed the chosen ones into `categoryIds`.\n- **Top-level book categories** of a marketplace (e.g. \"list the main Books categories on Amazon.de\"): `GET /categories?domainId=3&depth=2`.\n- **Walk down the tree** from a known node (e.g. \"what's under Cookbooks?\"): `GET /categories/children?domainId=1&catId=6`.\n- **Walk up the tree / breadcrumb** for a leaf node (e.g. \"where does cat 4546138031 sit?\"): `GET /categories/ancestors?domainId=8&catId=4546138031` → returns root → … → node, ordered by depth.\n\n### Shared response object — `CategoryBrowseNodeDto`\n\n| Field             | Type     | Notes                                                                                       |\n|-------------------|----------|---------------------------------------------------------------------------------------------|\n| `catId`           | long     | Amazon browse node ID. **This is the value to pass into `categoryIds` in book search.**     |\n| `parentCatId`     | long?    | Parent's `catId`. `null` for roots.                                                         |\n| `rootCatId`       | long     | Top-level ancestor's `catId` (e.g. the Books root for the marketplace).                     |\n| `name`            | string   | Localised name in the marketplace's language.                                               |\n| `contextFreeName` | string?  | Name without parent context (Keepa-provided). May be null.                                  |\n| `depth`           | int      | 0 = root. Top-level book categories are typically depth 2 (root → Categorie → top node).    |\n| `isFiction`       | bool     | Internal flag used by `excludeFiction` in book search. Only meaningful at depth=2 under Books root. |\n| `productCount`    | int?     | Approximate number of products in that node (Keepa-reported). May be null.                  |\n\n### 1. List categories at a depth (top-level Books browser)\n\n```\nGET /api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\n```\n\n| Param            | Type  | Default | Notes                                                                                                                                  |\n|------------------|-------|---------|----------------------------------------------------------------------------------------------------------------------------------------|\n| `domainId`       | int   | —       | Required. Marketplace ID (1-12, but only the 7 IDs in the marketplace table return data).                                              |\n| `depth`          | int?  | 2       | Hierarchy level. 0 = root. 2 = top-level book categories (\"Self-Help\", \"Cookbooks…\", etc.). Allowed 0-5.                               |\n| `includeFiction` | bool? | `false` | Whether to include nodes flagged as fiction (Literature & Fiction, Romance, Sci-Fi, Children's Books, Teen/YA, Comics, etc.).          |\n\n**Filters applied automatically (do not appear in params):** only browse nodes (`isBrowseNode=true`), and only nodes whose root is the **Books** root of the marketplace — i.e. non-book trees (toys, electronics) are excluded. Use this endpoint to enumerate the canonical KDP-relevant taxonomy.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nResponse envelope — `CategoriesApiResponse`:\n\n| Field           | Type    | Notes                                |\n|-----------------|---------|--------------------------------------|\n| `domainId`      | int     | Echo.                                |\n| `depth`         | int     | Echo (resolved default if omitted).  |\n| `returnedCount` | int     | Number of items in `results`.        |\n| `results`       | array   | `CategoryBrowseNodeDto[]`.           |\n\n### 2. Direct children of a category\n\n```\nGET /api/v1/categories/children?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                                            |\n|------------|------|----------------------------------------------------------------------------------|\n| `domainId` | int  | Required.                                                                        |\n| `catId`    | long | Required. Parent category's Amazon browse node ID.                               |\n\n**No implicit filters.** Returns ALL active direct children of the parent — including non-browse nodes and fiction nodes. Use it for true tree navigation regardless of book/non-book context.\n\nOrder: `productCount` DESC, then `name` ASC.\n\nReturns **404** if `catId` does not exist for the given marketplace. Do not retry on 404.\n\nResponse envelope — `CategoryChildrenApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `parentCatId`   | long   | Echo of the input `catId`.           |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n### 3. Search categories by name\n\n```\nGET /api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\n```\n\n| Param      | Type    | Default | Notes                                                                                                              |\n|------------|---------|---------|--------------------------------------------------------------------------------------------------------------------|\n| `domainId` | int     | —       | Required.                                                                                                          |\n| `q`        | string  | —       | Required. Case-insensitive substring match on `name`. Min **3** chars, max 100. Wildcards (`%`, `_`, `\\`) are treated as literals (escaped server-side). |\n| `limit`    | int?    | 50      | 1-200. Pushed down to SQL.                                                                                         |\n\n**No implicit filters.** Searches across the ENTIRE category tree of the marketplace (not just Books) — so a query like \"Sport\" on Amazon.it will surface both \"Sport e tempo libero\" (under Books) and the toy/apparel \"Sport\" nodes. The agent should filter client-side by `rootCatId` if the user only wants book categories.\n\nOrder: `productCount` DESC, then `depth` ASC, then `name` ASC.\n\nResponse envelope — `CategorySearchApiResponse`:\n\n| Field           | Type   | Notes                                |\n|-----------------|--------|--------------------------------------|\n| `domainId`      | int    | Echo.                                |\n| `query`         | string | Echo of `q`.                         |\n| `limit`         | int    | Effective limit applied.             |\n| `returnedCount` | int    |                                      |\n| `results`       | array  | `CategoryBrowseNodeDto[]`.           |\n\n**Tip:** if the user-typed term is broad (e.g. \"fiction\") and the default `limit=50` truncates likely candidates, raise `limit` to 200. If still not enough, refine the term or use `/categories?depth=2` instead.\n\n### 4. Ancestors / breadcrumb of a category\n\n```\nGET /api/v1/categories/ancestors?domainId={..}&catId={..}\n```\n\n| Param      | Type | Notes                                                  |\n|------------|------|--------------------------------------------------------|\n| `domainId` | int  | Required.                                              |\n| `catId`    | long | Required. Category whose breadcrumb to retrieve.       |\n\nReturns the full ancestor chain **including the node itself**, ordered by `depth` ASC. No implicit filters (intermediate non-browse / promotional nodes are included).\n\nReturns **404** if `catId` does not exist for the marketplace.\n\nResponse envelope — `CategoryAncestorsApiResponse`:\n\n| Field           | Type   | Notes                                                                |\n|-----------------|--------|----------------------------------------------------------------------|\n| `domainId`      | int    | Echo.                                                                |\n| `catId`         | long   | Echo.                                                                |\n| `returnedCount` | int    | Length of the chain (includes the requested node).                   |\n| `results`       | array  | `CategoryBrowseNodeDto[]` from root (depth=0) to the node itself.    |\n\n### Example workflows\n\n**Resolve \"manga\" on Amazon.it then search books:**\n\n```bash\n# 1. find candidate cat_ids\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://beyondbsr.com/api/v1/categories/search?domainId=8&q=manga&limit=10\"\n\n# 2. inspect the breadcrumb of a candidate to confirm it's under Libri\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://beyondbsr.com/api/v1/categories/ancestors?domainId=8&catId=4546138031\"\n\n# 3. drill down children if needed\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://beyondbsr.com/api/v1/categories/children?domainId=8&catId=4546138031\"\n\n# 4. plug the chosen cat_id(s) into book search\ncurl -X POST -H \"X-API-Key: $BOOKSEARCH_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"domainId\":8,\"categoryIds\":[4546138031],\"limit\":50}' \\\n  \"https://beyondbsr.com/api/v1/books/search\"\n```\n\n**List top-level Books categories on Amazon.com (non-fiction only, default):**\n\n```bash\ncurl -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  \"https://beyondbsr.com/api/v1/categories?domainId=1&depth=2\"\n```\n\n### Status codes (categories endpoints)\n\n| Code | Meaning                          | Notes                                                                                       |\n|------|----------------------------------|---------------------------------------------------------------------------------------------|\n| 200  | OK                               | `results` may be empty (e.g. no children, no matches, marketplace not seeded).              |\n| 400  | Validation failed                | `ValidationProblemDetails`. Common causes: `domainId` out of range, `q` < 3 chars, `limit` out of 1-200, `depth` out of 0-5. |\n| 401  | Auth failed                      | Same handling as book search — stop, report misconfigured key.                              |\n| 404  | `catId` not found for `domainId` | Empty body. Only on `/children` and `/ancestors`. Do not retry the same pair.               |\n| 429  | Rate limit                       | Honour `Retry-After`. Same key budget as book search (30 req/min).                          |\n| 500  | Server error                     | Retry once, then escalate.                                                                  |\n\n### Caveats\n\n- **Read-only.** No POST/PUT/DELETE on categories.\n- **`catId` vs internal id.** The wire contract exposes only Amazon's `catId` (browse node ID). The internal database `id` is never surfaced and is not interchangeable.\n- **`depth=2` ≠ \"top-level under Books\" universally.** Most marketplaces seed Books root at depth=0 → \"Categorie/Categories\" at depth=1 → top nodes at depth=2. If `/categories?depth=2` returns unexpectedly few rows for a marketplace, retry with `depth=1`.\n- **`excludeFiction` semantics propagate.** A node's `isFiction=true` here is exactly what `excludeFiction` in book search rolls up on. If a user complains that a fiction sub-genre isn't being excluded, the agent can spot-check via `/categories/ancestors?catId=…` whether the depth-2 ancestor is flagged.\n- **Children endpoint includes non-browse nodes.** Some Amazon nodes are promotional or grouping shells (`isBrowseNode=false`). They cannot be used as `categoryIds` filters in book search. Skip them or warn the user.\n\n## Status codes & error handling\n\n| Code | Meaning                | Body                                       | Agent action                                                                  |\n|------|------------------------|--------------------------------------------|-------------------------------------------------------------------------------|\n| 200  | OK (may be empty list) | `BookSearchApiResponse`                    | Parse `results`. Empty array = no matches, not an error.                      |\n| 400  | Validation failed      | `ValidationProblemDetails` (RFC 7807)      | Read the `errors` map, fix the body, do not retry blindly. Surface the issue. |\n| 401  | Auth failed            | empty + `WWW-Authenticate: ApiKey realm=\"BeyondBSR\"` | Stop. Report misconfigured `BOOKSEARCH_API_KEY`. Do not retry.       |\n| 429  | Rate limit exceeded    | `\"Rate limit exceeded. Please try again later.\"` + `Retry-After` header | Honour `Retry-After`. Back off. Do not hammer the endpoint. |\n| 500  | Unhandled server error | `ProblemDetails` JSON                      | Retry once after a few seconds. If it persists, escalate to the user.         |\n\n### Example 400 body\n\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.1\",\n  \"title\": \"One or more validation errors occurred.\",\n  \"status\": 400,\n  \"errors\": {\n    \"DomainId\": [\"DomainId must be between 1 and 12.\"],\n    \"Limit\": [\"Limit must be between 1 and 300.\"]\n  }\n}\n```\n\n## Sorting & pagination notes\n\n- Default sort: `ORDER BY <chosen BSR column> ASC` (lower BSR = better seller appears first).\n- Historical mode (`bsrType=Historical` + `bsrYear`/`bsrMonth`): sorted by the historical monthly BSR rank ASC.\n- Tie-breaker is non-deterministic — books with identical BSR may appear in different orders across page fetches. Warn the user if they need a perfectly stable ordering.\n- No `orderBy` parameter is exposed.\n\n## Limits\n\n- **Rate limit**: 30 requests/minute per API key. Shared across all callers using the same key.\n- **Body size**: 128 KB max.\n- **Page size**: 300 results max per request (`limit ≤ 300`).\n- **Offset cap**: 100,000 (deep pagination beyond this is not supported — refine filters instead).\n\nFile v1.0.7:_meta.json\n\n{\n  \"ownerId\": \"kn71bkxxw77zyvcjdxzqpwcs658570bb\",\n  \"slug\": \"booksearch-api\",\n  \"version\": \"1.0.7\",\n  \"publishedAt\": 1778846248698\n}\n\nFile v1.0.7:skill-card.md\n\n## Description: <br>\nSearches the BeyondBSR public API for Amazon KDP book data, single-ASIN BSR history, and category taxonomy lookups across supported marketplaces. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ramius88](https://clawhub.ai/user/ramius88) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal users and agents use this skill to research Amazon book niches, estimate sales and royalties, compare BSR windows, resolve category IDs, and produce category-grouped market-opportunity summaries from BeyondBSR data. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires a sensitive BeyondBSR API key. <br>\nMitigation: Provide the key through the BOOKSEARCH_API_KEY environment variable and do not print, log, echo, or include it in user-facing output. <br>\nRisk: The BeyondBSR API is described as private beta access and may reject missing or invalid credentials. <br>\nMitigation: On a 401 response, stop without retrying and tell the user to check the BOOKSEARCH_API_KEY environment variable. <br>\nRisk: Search results, BSR fields, category paths, and sales or revenue estimates have documented data caveats. <br>\nMitigation: Present BSR and revenue values as estimates, preserve the selected marketplace and BSR window, and call out documented caveats when the user asks for strict historical or category analysis. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ramius88/booksearch-api) <br>\n- [BeyondBSR book search endpoint](https://beyondbsr.com/api/v1/books/search) <br>\n- [BeyondBSR categories endpoint](https://beyondbsr.com/api/v1/categories) <br>\n- [BeyondBSR category search endpoint](https://beyondbsr.com/api/v1/categories/search) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance] <br>\n**Output Format:** [Markdown guidance with JSON request bodies, endpoint details, and curl examples] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires BOOKSEARCH_API_KEY and returns or summarizes JSON API responses from BeyondBSR.] <br>\n\n## Skill Version(s): <br>\n1.0.7 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nArchive v1.0.6: 2 files, 12691 bytes\n\nFiles: SKILL.md (39694b), _meta.json (133b)\n\nFile v1.0.6:SKILL.md\n\n---\nname: booksearch-api\ndescription: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (browse nodes) for any supported marketplace. Each book-search result now ships its full root → leaf category ancestor chain(s) inline, so the skill can aggregate market-opportunity reports by macro/sub category without follow-up calls. Use this skill whenever the user wants to discover, filter, or research self-published or traditionally published books on Amazon by BSR, category, keyword, royalty range, rating, reviews, publication date, binding type, or marketplace (US, UK, DE, FR, IT, ES, CA); when the user wants the BSR timeline of a specific ASIN over the last N days; when the user wants to look up Amazon category codes (browse node IDs), walk the category tree (children, ancestors/breadcrumb), or search categories by name to use as filters in book search; or when the user wants to group/aggregate search results by macro category (e.g. \"how many Personal Finance opportunities, broken down by sub-category?\"). Typical intents include KDP niche research, low-competition book discovery, sales estimation, royalty/revenue projection, competitor analysis, paperback/hardcover filtering, bulk listing of books matching numeric/textual criteria, single-ASIN BSR history charts, resolving a human-readable category name (e.g. \"manga\", \"self-help\") into the Amazon `catId` to pass to `categoryIds` in book search, and producing category-grouped market-opportunity summaries from a single search response. Do not use for price-history timelines, review/rating timelines, or account/user data — only book search, BSR history, and category browsing are exposed.\nmetadata:\n  clawdbot:\n    requires:\n      env:\n        - BOOKSEARCH_API_KEY\n---\n\n# BookSearch API\n\n## ⚠️ API Access & Beta Program\n\nThe BeyondBSR BookSearch API is currently in **private beta**. This skill requires an API key (`BOOKSEARCH_API_KEY`) which is **not publicly available** at this time.\n\nUsers interested in accessing Amazon KDP book data (BSR history, reviews, categories, keyword research) can apply to the early adopter program by contacting **support@beyondbsr.com**. Requests are reviewed individually and approved keys are issued on a case-by-case basis.\n\nWithout a valid key, all endpoints below will return `401 Unauthorized`.\n\n---\n\nProgrammatic search over the BeyondBSR book catalogue, BSR history retrieval for a single book, and Amazon category taxonomy browsing. Six endpoints, JSON in / JSON out, API-key auth.\n\n## When to use this skill\n\nUse it when the user asks to:\n\n- Find books matching numeric filters: BSR range, rating, reviews count, royalty, page count age, publication recency.\n- Discover niches by keyword inclusion/exclusion or Amazon category IDs.\n- Filter by marketplace (Amazon.com, .co.uk, .de, .fr, .it, .es, .ca).\n- Distinguish self-publishers from traditional publishers.\n- Estimate sales (daily / weekly / monthly / quarterly) and revenue per copy.\n- Compare BSR averages across multiple time windows (7d / 30d / 90d / 180d / 365d).\n- Retrieve the **BSR timeline** of a single book (by `domainId` + `asin`) over the last N days, e.g. for charting rank evolution.\n- **Resolve a category name into an Amazon `catId`** (browse node ID), look up a category's direct children, walk its breadcrumb up to the root, or list top-level book categories for a marketplace — to feed `categoryIds` into book search, or just to explore the taxonomy.\n\n**Do NOT use** for: price-history charts, review/rating timelines, account/user data, or anything not in the response schemas below. Those are out of scope.\n\n## Endpoints\n\n```\nPOST https://beyondbsr.com/api/v1/books/search\nGET  https://beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nGET  https://beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\nGET  https://beyondbsr.com/api/v1/categories/children?domainId={..}&catId={..}\nGET  https://beyondbsr.com/api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\nGET  https://beyondbsr.com/api/v1/categories/ancestors?domainId={..}&catId={..}\nContent-Type: application/json   (book search only)\nX-API-Key: $BOOKSEARCH_API_KEY\n```\n\n## Authentication\n\n- Read the key from the `BOOKSEARCH_API_KEY` env var. Format: `bbsr_live_<43-char-base64url>`.\n- **Never** print, echo, log, or include the key in any user-facing output. Never paste it into another tool's input.\n- On `401 Unauthorized`: do not retry. Report \"API key missing or invalid — check `BOOKSEARCH_API_KEY` env var\" and stop.\n- Send `X-API-Key` exactly once. Multi-valued headers are rejected.\n\n## Marketplace domains\n\nMap natural-language marketplace references (e.g. \"Amazon.de\", \"the UK store\", \"amazon italia\") to `domainId` using this table:\n\n| domainId | locale | country | name           |\n|----------|--------|---------|----------------|\n| 1        | com    | US      | United States  |\n| 2        | co.uk  | GB      | United Kingdom |\n| 3        | de     | DE      | Germany        |\n| 4        | fr     | FR      | France         |\n| 6        | ca     | CA      | Canada         |\n| 8        | it     | IT      | Italy          |\n| 9        | es     | ES      | Spain          |\n\nIDs `5` and `7` are intentional gaps in the dataset — do not invent them. The validator technically accepts `1–12`, but only the seven IDs above are guaranteed to return data. If the user asks for a marketplace not listed (e.g. Japan, Australia), tell them it is not currently supported.\n\n## Request schema\n\nAll fields are optional except `domainId`. Enums accept either the string name (e.g. `\"Weekly\"`) or the integer value.\n\n### Required\n\n| Field      | Type | Constraint                  |\n|------------|------|-----------------------------|\n| `domainId` | int  | One of the 7 IDs in the marketplace table above (validator allows 1–12). |\n\n### BSR filters\n\n| Field      | Type        | Default   | Notes                                                                       |\n|------------|-------------|-----------|-----------------------------------------------------------------------------|\n| `bsrType`  | enum        | `Weekly`  | `Historical(-1)`, `Current(0)`, `Weekly(7)`, `Days30(8)`, `Days90(9)`, `Days180(10)`, `Days365(11)` |\n| `bsrMin`   | int?        | 1         | ≥ 1                                                                         |\n| `bsrMax`   | int?        | 100000    | ≥ 1, `bsrMin ≤ bsrMax`                                           \n\nArchive v1.0.5: 2 files, 11060 bytes\n\nFiles: SKILL.md (35494b), _meta.json (133b)\n\nArchive v1.0.4: 2 files, 7902 bytes\n\nFiles: SKILL.md (22273b), _meta.json (133b)\n\nArchive v1.0.3: 2 files, 7679 bytes\n\nFiles: SKILL.md (21776b), _meta.json (133b)\n\nArchive v1.0.2: 2 files, 7209 bytes\n\nFiles: SKILL.md (20612b), _meta.json (133b)\n\nArchive v1.0.1: 2 files, 5743 bytes\n\nFiles: SKILL.md (15659b), _meta.json (133b)\n\nArchive v1.0.0: 2 files, 5758 bytes\n\nFiles: SKILL.md (15690b), _meta.json (133b)","readmeExcerpt":"Skill: BeyondBSR - BookSearch API Owner: ramius88 Summary: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br... Tags: latest:1.0.9 Version history: v1.0.9 | 2026-06-03T11:57:49.641Z | user - Removed the skill-card.md file. - No changes to functionality or documentation content. v1.0.8 | 2026-06-01T13:57:00.217Z","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"POST https://api.beyondbsr.com/api/v1/books/search\nGET  https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365}\nGET  https://api.beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false}\nGET  https://api.beyondbsr.com/api/v1/categories/children?domainId={..}&catId={..}\nGET  https://api.beyondbsr.com/api/v1/categories/search?domainId={..}&q={..}&limit={1..200}\nGET  https://api.beyondbsr.com/api/v1/categories/ancestors?domainId={..}&catId={..}\nContent-Type: application/json   (book search only)\nX-API-Key: $BOOKSEARCH_API_KEY"},{"language":"json","snippet":"{\n  \"domainId\": 4,\n  \"bsrType\": \"Days90\",\n  \"bsrMin\": 1,\n  \"bsrMax\": 50000,\n  \"bindingType\": \"Paperback\",\n  \"publisherType\": \"SelfPublishersOnly\",\n  \"ratingMin\": 4.0,\n  \"reviewsMin\": 10,\n  \"interiorType\": \"BlackWhite\",\n  \"vatType\": \"Reduced\",\n  \"royaltyMin\": 2.50,\n  \"royaltyMax\": 15.00,\n  \"monthsSincePublicationMax\": 24,\n  \"includePreOrders\": false,\n  \"includeKeywords\": [\"journal\", \"notebook\"],\n  \"excludeKeywords\": [\"coloring\"],\n  \"categoryIds\": [266162, 3248921],\n  \"excludeFiction\": false,\n  \"limit\": 50,\n  \"offset\": 0\n}"},{"language":"bash","snippet":"curl -X POST \"https://api.beyondbsr.com/api/v1/books/search\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -d '{"},{"language":"bash","snippet":"curl -X POST \"https://api.beyondbsr.com/api/v1/books/search\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: $BOOKSEARCH_API_KEY\" \\\n  -d '{\n    \"domainId\": 4,\n    \"bsrType\": \"Days90\",\n    \"bsrMax\": 50000,\n    \"bindingType\": \"Paperback\",\n    \"publisherType\": \"SelfPublishersOnly\",\n    \"ratingMin\": 4.0,\n    \"limit\": 50\n  }'"},{"language":"js","snippet":"// macro (depth=2) -> { name, books, subniches: Map<catId, { name, books, monthlyRevCents }> }\nconst report = new Map();\n\nfor (const book of response.results) {\n  const seenMacros  = new Set();   // dedupe within the same book\n  const seenNiches  = new Set();\n\n  for (const chain of book.categoryPaths) {        // may be [] for un-enriched books\n    const macro = chain.find(n => n.depth === 2);\n    const niche = chain.find(n => n.depth === 3) ?? chain.find(n => n.depth === 4);\n    if (!macro) continue;\n\n    if (!seenMacros.has(macro.catId)) {\n      seenMacros.add(macro.catId);\n      const m = report.get(macro.catId) ?? { name: macro.name, books: 0, subniches: new Map() };\n      m.books++;\n      report.set(macro.catId, m);\n    }\n    if (niche && !seenNiches.has(niche.catId)) {\n      seenNiches.add(niche.catId);\n      const m = report.get(macro.catId);\n      const n = m.subniches.get(niche.catId) ?? { name: niche.name, books: 0, monthlyRevCents: 0 };\n      n.books++;\n      // pick whichever revenue field matches the user's interior/VAT context\n      n.monthlyRevCents += book.monthlyRevenueBlackReducedVatCents ?? 0;\n      m.subniches.set(niche.catId, n);\n    }\n  }\n}"},{"language":"json","snippet":"{\n  \"asin\": \"1234567890\",\n  \"title\": \"Quick Weeknight Dinners\",\n  \"categories\": [4259, 9876],\n  \"categoryPaths\": [\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",             \"depth\": 1 },\n      { \"catId\": 6,      \"name\": \"Cookbooks, Food & Wine\",\"depth\": 2 },\n      { \"catId\": 17,     \"name\": \"Quick & Easy\",         \"depth\": 3 },\n      { \"catId\": 4259,   \"name\": \"General\",              \"depth\": 4 }\n    ],\n    [\n      { \"catId\": 283155, \"name\": \"Books\",                       \"depth\": 0 },\n      { \"catId\": 1000,   \"name\": \"Subjects\",                    \"depth\": 1 },\n      { \"catId\": 10,     \"name\": \"Health, Fitness & Dieting\",   \"depth\": 2 },\n      { \"catId\": 9876,   \"name\": \"Diet & Weight Loss\",          \"depth\": 3 }\n    ]\n  ]\n}"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: booksearch-api\ndescription: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (browse nodes) for any supported marketplace. Each book-search result now ships its full root → leaf category ancestor chain(s) inline, so the skill can aggregate market-opportunity reports by macro/sub category without follow-up calls. Use this skill whenever the user wants to discover, filter, or research self-published or traditionally published books on Amazon by BSR, category, keyword, royalty range, rating, reviews, publication date, binding type, or marketplace (currently only the US and FR Amazon marketplaces are populated with data — more coming soon); when the user wants the BSR timeline of a specific ASIN over the last N days; when the user wants to look up Amazon category codes (browse node IDs), walk the category tree (children, ancestors/breadcrumb), or search categories by name to use as filters in book search; or when the user wants to group/aggregate search results by macro category (e.g. \"how many Personal Finance opportunities, broken down by sub-category?\"). Typical intents include KDP niche research, low-competition book discovery, sales estimation, royalty/revenue projection, competitor analysis, paperback/hardcover filtering, bulk listing of books matching numeric/textual criteria, single-ASIN BSR history charts, resolving a human-readable category name (e.g. \"manga\", \"self-help\") into the Amazon `catId` to pass to `categoryIds` in book search, and producing category-grouped market-opportunity summaries from a single search response. Do not use for price-history timelines, review/rating timelines, or account/user data — only book search, BSR history, and category browsing are exposed.\nmetadata:\n  clawdbot:\n    requires:\n      env:\n        - BOOKSEARCH_API_KEY\n---\n\n# BookSearch API\n\n## ⚠️ API Access & Beta Program\n\nThe BeyondBSR BookSearch API is currently in **private beta**. This skill requires an API key (`BOOKSEARCH_API_KEY`) which is **not publicly available** at this time.\n\nUsers interested in accessing Amazon KDP book data (BSR history, reviews, categories, keyword research) can apply to the early adopter program by contacting **support@beyondbsr.com**. Requests are reviewed individually and approved keys are issued on a case-by-case basis.\n\nWithout a valid key, all endpoints below will return `401 Unauthorized`.\n\n---\n\nProgrammatic search over the BeyondBSR book catalogue, BSR history retrieval for a single book, and Amazon category taxonomy browsing. Six endpoints, JSON in / JSON out, API-key auth.\n\n## When to use this skill\n\nUse it when the user asks to:\n\n- Find books matching numeric filters: BSR range, rating, reviews count, royalty, page count age, publication recency.\n- Discover niches by keyword inclusion/exclusion or Amazon category IDs.\n- Filter by marketplace (currently US / Amazon.com and FR / Amazon.fr only).\n- Distin"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn71bkxxw77zyvcjdxzqpwcs658570bb\",\n  \"slug\": \"booksearch-api\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1780487869641\n}"},{"path":"skill-card.md","content":"## Description:\n\nSearches the BeyondBSR public API for Amazon KDP books, retrieves single-ASIN BSR history, and browses Amazon category taxonomy for supported marketplaces.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ramius88](https://clawhub.ai/user/ramius88)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users and developers use this skill for KDP niche research, competitor analysis, sales and royalty estimates, BSR history checks, and Amazon category lookup through the BeyondBSR API.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires a private beta API key and sends book-search queries to api.beyondbsr.com.\n\nMitigation: Install only when the publisher is trusted, keep BOOKSEARCH_API_KEY out of user-facing output, and avoid exposing it in logs or screenshots.\n\nRisk: The skill depends on external API availability, authorization, rate limits, and marketplace coverage.\n\nMitigation: Handle 401, 429, 404, and validation errors as documented, and limit use to populated US and FR marketplace data unless newer server evidence says otherwise.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ramius88/skills/booksearch-api)\n- [Publisher Profile](https://clawhub.ai/user/ramius88)\n- [BeyondBSR Book Search API](https://api.beyondbsr.com/api/v1/books/search)\n- [BeyondBSR BSR History API](https://api.beyondbsr.com/api/v1/books/{domainId}/{asin}/bsr-history?days={1..365})\n- [BeyondBSR Categories API](https://api.beyondbsr.com/api/v1/categories?domainId={..}&depth={0..5}&includeFiction={true|false})\n\n## Skill Output:\n\n**Output Type(s):** [API Calls, Markdown, Code, Shell commands, Guidance]\n\n**Output Format:** [Markdown with JSON and shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include API request bodies, parsed result summaries, BSR timelines, category mappings, and market-opportunity analysis.]\n\n## Skill Version(s):\n\n1.0.9 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br... Skill: BeyondBSR - BookSearch API Owner: ramius88 Summary: Search Amazon KDP books on the BeyondBSR public API, retrieve BSR (Best Sellers Rank) history for a single book, and explore the Amazon category taxonomy (br... Tags: latest:1.0.9 Version history: v1.0.9 | 2026-06-03T11:57:49.641Z | user - Removed the skill-card.md file. - No changes to functionality or documentation content. v1.0.8 | 2026-06-01T13:57:00.217Z","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1458,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T12:16:33.202Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:43:59.028Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}