{"_id":"@alterlab/sdk","_rev":"4-d651bba231c0e6e709e32f1f8ee6c3ae","name":"@alterlab/sdk","dist-tags":{"latest":"2.4.0"},"versions":{"2.0.1":{"name":"@alterlab/sdk","version":"2.0.1","_id":"@alterlab/sdk@2.0.1","maintainers":[{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"}],"dist":{"shasum":"fb6d02280e064ea32f0e0a935ab596b7975360ec","tarball":"https://registry.npmjs.org/@alterlab/sdk/-/sdk-2.0.1.tgz","fileCount":10,"integrity":"sha512-m1l9+H0bIfg/aiDlylEQ6/l92lNs/tN4bmRtz/KJ6b3g83psEm9An6MRuFGQ8zDhPY1QzS9oW84CkJyjZkizqw==","signatures":[{"sig":"MEUCIG2Vh2N5eKIQ4jX2y37qrcxtdhrvOJbEp4Da0waozo2IAiEA0UEQt6OA9k7Yr+drsSH4eKEYRv7yVKeEMU0J6k4F95o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34944},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"5a6c4531a789137b6baf9a23f0391343575f4090","scripts":{"test":"jest","build":"tsc"},"_npmUser":{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"},"_npmVersion":"10.9.3","description":"AlterLab Node.js SDK","directories":{},"_nodeVersion":"22.19.0","dependencies":{"axios":"^1.6.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.2","@types/node":"^20.10.4"},"_npmOperationalInternal":{"tmp":"tmp/sdk_2.0.1_1769390925019_0.9047669941387622","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@alterlab/sdk","version":"2.1.0","_id":"@alterlab/sdk@2.1.0","maintainers":[{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"}],"dist":{"shasum":"4b5c800d09a0104d2bb25fa62657680e152c9c56","tarball":"https://registry.npmjs.org/@alterlab/sdk/-/sdk-2.1.0.tgz","fileCount":8,"integrity":"sha512-mZ3dk2E0bNzcZRyw0ulzV7xwjKnfMVWtKKWoM2oM1rbRJY+MsG0/hG7nPCFuYOnZTDbZcI42ozk0khx9zqD7ww==","signatures":[{"sig":"MEYCIQCGkvdsUl+lIFlQB1+S1kxdhAnorUj6PXpmj+FDwpgd2gIhAPkDlxtki8W/BdzIROCAjuV2Bhh4KuAbzjPCoTA5lWdP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32134},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"63b735e8aad3c0b6203a7604ee30b415f622b2d6","scripts":{"test":"jest","build":"tsc"},"_npmUser":{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"},"_npmVersion":"10.8.2","description":"AlterLab Node.js SDK","directories":{},"_nodeVersion":"18.20.8","dependencies":{"axios":"^1.6.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.2","@types/node":"^20.10.4"},"_npmOperationalInternal":{"tmp":"tmp/sdk_2.1.0_1774798931892_0.40109553756683747","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@alterlab/sdk","version":"2.2.0","_id":"@alterlab/sdk@2.2.0","maintainers":[{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"}],"homepage":"https://github.com/RapierCraft/AlterLab#readme","bugs":{"url":"https://github.com/RapierCraft/AlterLab/issues"},"dist":{"shasum":"2db2bb5a09f1cdf0241d46e55d02a96877f75b91","tarball":"https://registry.npmjs.org/@alterlab/sdk/-/sdk-2.2.0.tgz","fileCount":18,"integrity":"sha512-iUEwwT0I1e+tnoPURXZG1w5m/NLnFDKXfOFD1zerCleUU7eOrwtZjY+MUNcP4Ht8jPfsNW2YhRegmTVIMA15FQ==","signatures":[{"sig":"MEUCIQD1xWpZFizd4eInPCOktQE81K8VUE90jpLCuysUtMzD+wIgK3a2740j5l/4hfqNoI+K2h5CgAL5SbL5CT2WI2SyEOA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128080},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"43c26c9bd501f666934b554fe7dd97cd191c4a1c","scripts":{"test":"jest","build":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:37b90ed3-dae4-431a-a57e-b67211100616"}},"repository":{"url":"git+https://github.com/RapierCraft/AlterLab.git","type":"git","directory":"sdk/node"},"_npmVersion":"11.11.0","description":"AlterLab Node.js SDK","directories":{},"_nodeVersion":"24.14.1","dependencies":{"axios":"^1.6.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.2","@types/node":"^20.10.4"},"_npmOperationalInternal":{"tmp":"tmp/sdk_2.2.0_1778131264573_0.8223562254652614","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@alterlab/sdk","version":"2.4.0","description":"AlterLab Node.js SDK","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","test":"jest"},"dependencies":{"axios":"^1.6.2"},"devDependencies":{"@types/node":"^20.10.4","typescript":"^5.3.2"},"repository":{"type":"git","url":"git+https://github.com/RapierCraftStudios/AlterLab.git","directory":"sdk/node"},"gitHead":"921dad98fa2c988801a63e220f17a0e1ff0a57e9","_id":"@alterlab/sdk@2.4.0","bugs":{"url":"https://github.com/RapierCraftStudios/AlterLab/issues"},"homepage":"https://github.com/RapierCraftStudios/AlterLab#readme","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-XTvcfcP/2sh/12zMJ41HPnE7Om7U+W+zEAq3/Co7/TDUz64f6q245vgKQYB0MZmVBUO6gDaqcoLb1gldWj4b3Q==","shasum":"828e37655900b3f2c8392428b5ce17759ac457cc","tarball":"https://registry.npmjs.org/@alterlab/sdk/-/sdk-2.4.0.tgz","fileCount":18,"unpackedSize":200247,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDOfJXCKCVI4iev5JS+cTNnubPbT9l93Sd7jVKITD1PwQIgWnUYrAf2h5PDxVDEQlTH0uPKm4AAA+w1G8kApeytD6I="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:37b90ed3-dae4-431a-a57e-b67211100616"}},"directories":{},"maintainers":[{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_2.4.0_1780486229064_0.1478534198725341"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-26T01:28:44.915Z","modified":"2026-06-03T11:30:29.364Z","2.0.1":"2026-01-26T01:28:45.157Z","2.1.0":"2026-03-29T15:42:12.052Z","2.2.0":"2026-05-07T05:21:04.719Z","2.4.0":"2026-06-03T11:30:29.228Z"},"bugs":{"url":"https://github.com/RapierCraftStudios/AlterLab/issues"},"homepage":"https://github.com/RapierCraftStudios/AlterLab#readme","repository":{"type":"git","url":"git+https://github.com/RapierCraftStudios/AlterLab.git","directory":"sdk/node"},"description":"AlterLab Node.js SDK","maintainers":[{"name":"rapiercraft","email":"rapiercraftstudios@gmail.com"}],"readme":"# @alterlab/sdk\n\nThe official Node.js/TypeScript client for the [AlterLab](https://alterlab.io) web scraping API. Scrape any website with anti-bot bypass, structured extraction, and automatic tier escalation.\n\n[![npm version](https://img.shields.io/npm/v/@alterlab/sdk)](https://www.npmjs.com/package/@alterlab/sdk)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)\n\n## Installation\n\n```bash\nnpm install @alterlab/sdk\n```\n\n## Quick Start\n\n```typescript\nimport { AlterLab } from \"@alterlab/sdk\";\n\nconst client = new AlterLab({ apiKey: \"sk_test_...\" });\n\nconst result = await client.scrape({ url: \"https://example.com\" });\nconsole.log(result.content);\n```\n\n## Methods\n\n### `scrape(request)`\n\nScrape a single URL.\n\n```typescript\n// Simple scrape\nconst result = await client.scrape({ url: \"https://example.com\" });\n\n// JavaScript rendering with screenshot\nconst result = await client.scrape({\n  url: \"https://example.com\",\n  mode: \"js\",\n  render_js: true,\n  screenshot: true,\n});\nconsole.log(result.screenshot_url);\n\n// Structured extraction\nconst result = await client.scrape({\n  url: \"https://example.com/product\",\n  extraction_profile: \"product\",\n});\nconsole.log(result.extracted_data);\n\n// With JSON schema extraction\nconst result = await client.scrape({\n  url: \"https://example.com/product\",\n  extraction_schema: {\n    type: \"object\",\n    properties: {\n      title: { type: \"string\" },\n      price: { type: \"number\" },\n    },\n  },\n});\n```\n\n**`ScrapeRequest` fields:**\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `url` | `string` | required | URL to scrape |\n| `mode` | `string` | `\"auto\"` | `auto`, `html`, `js`, `pdf`, `ocr` |\n| `sync` | `boolean` | `true` | Wait for result (false returns job_id) |\n| `render_js` | `boolean` | `false` | Enable JavaScript rendering (+3 credits) |\n| `screenshot` | `boolean` | `false` | Capture screenshot (+1 credit, needs `render_js`) |\n| `markdown` | `boolean` | `false` | Convert to markdown (free) |\n| `wait_for` | `string` | — | CSS selector to wait for (JS mode) |\n| `timeout` | `number` | — | Request timeout in seconds |\n| `force_refresh` | `boolean` | `false` | Bypass cache |\n| `extraction_schema` | `object` | — | JSON Schema for structured extraction |\n| `extraction_prompt` | `string` | — | Natural language extraction instructions |\n| `extraction_profile` | `string` | — | `auto`, `product`, `article`, `job_posting`, `faq`, `recipe`, `event` |\n| `max_credits` | `number` | — | Maximum credits for this request |\n| `max_tier` | `string` | — | Maximum tier: `0.5`, `1`, `1.5`, `2`, `3`, `4` |\n\n### `crawl(request)`\n\nStart a multi-page website crawl. Returns immediately with a `crawl_id`.\n\n```typescript\nconst crawl = await client.crawl({\n  url: \"https://example.com\",\n  maxPages: 200,\n  maxDepth: 2,\n  includePatterns: [\"/blog/*\"],\n  formats: [\"markdown\"],\n});\nconsole.log(`Crawl ID: ${crawl.crawl_id}`);\nconsole.log(`Estimated pages: ${crawl.estimated_pages}`);\n```\n\n**`CrawlRequest` fields:**\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `url` | `string` | required | Starting URL |\n| `maxPages` | `number` | `50` | Maximum pages (1-100,000) |\n| `maxDepth` | `number` | `3` | Link-following depth (0-50) |\n| `includePatterns` | `string[]` | — | Glob patterns to include |\n| `excludePatterns` | `string[]` | — | Glob patterns to exclude |\n| `formats` | `string[]` | — | Output formats per page |\n| `extractionSchema` | `object` | — | JSON Schema for each page |\n| `extractionProfile` | `string` | — | Extraction profile for each page |\n| `webhookUrl` | `string` | — | Webhook for `crawl.completed` |\n| `respectRobots` | `boolean` | `true` | Respect robots.txt |\n| `includeSubdomains` | `boolean` | `false` | Follow subdomain links |\n| `costControls` | `object` | — | `{ maxCredits, maxTier, forceTier }` |\n\n### `crawlAndWait(request, options?)`\n\nStart a crawl and poll until all pages are done.\n\n```typescript\nconst results = await client.crawlAndWait(\n  {\n    url: \"https://example.com\",\n    maxPages: 50,\n    formats: [\"markdown\"],\n  },\n  { pollTimeout: 300000 },\n);\nconsole.log(`Pages: ${results.completed}/${results.total}`);\nfor (const page of results.pages ?? []) {\n  console.log(`  ${page.url}: ${page.status}`);\n}\n```\n\n### `getCrawl(crawlId, includeResults?)`\n\nGet crawl progress. Pass `true` to include per-page results.\n\n```typescript\nconst status = await client.getCrawl(crawlId);\nconsole.log(`${status.completed}/${status.total} pages done`);\n```\n\n### `cancelCrawl(crawlId)`\n\nCancel a running crawl. Credits for unprocessed pages are refunded.\n\n```typescript\nconst result = await client.cancelCrawl(crawlId);\nconsole.log(`Refunded ${result.credits_refunded} credits`);\n```\n\n### `batchScrape(requests, webhookUrl?)`\n\nSubmit multiple scrape requests as a batch.\n\n```typescript\nconst batch = await client.batchScrape(\n  [\n    { url: \"https://example.com/page1\", mode: \"html\" },\n    { url: \"https://example.com/page2\", mode: \"js\" },\n  ],\n  \"https://myapp.com/webhook\",\n);\n\nfor (const jobId of batch.job_ids) {\n  const result = await client.waitForJob(jobId);\n  console.log(result.result?.content.length);\n}\n```\n\n### `estimateCost(request)`\n\nGet a cost estimate before scraping.\n\n```typescript\nconst estimate = await client.estimateCost({\n  url: \"https://example.com\",\n  mode: \"js\",\n});\nconsole.log(`Estimated: ${estimate.estimated_credits} credits`);\nconsole.log(`Max possible: ${estimate.max_possible_credits} credits`);\n```\n\n### `getUsage()`\n\nCheck your credit balance and usage stats.\n\n```typescript\nconst usage = await client.getUsage();\nconsole.log(`Credits remaining: ${usage.credits_available}`);\nconsole.log(`Plan: ${usage.plan}`);\n```\n\n### `waitForJob(jobId, options?)`\n\nPoll an async job until completion with exponential backoff.\n\n```typescript\nconst result = await client.waitForJob(jobId, {\n  pollInterval: 2000,\n  pollTimeout: 300000,\n  backoffMultiplier: 1.5,\n  maxInterval: 30000,\n});\n```\n\n### `getJobStatus(jobId)`\n\nCheck job status without waiting.\n\n```typescript\nconst job = await client.getJobStatus(jobId);\nconsole.log(`Status: ${job.status}`);\n```\n\n## Client Options\n\n```typescript\nconst client = new AlterLab({\n  apiKey: \"sk_test_...\",                    // Required\n  baseUrl: \"https://api.alterlab.io\",       // Default API URL\n  maxRetries: 3,                            // Retry count for transient failures\n  retryDelay: 1000,                         // Initial retry delay in ms (exponential backoff)\n  logger: silentLogger,                     // Custom logger (or silentLogger to disable)\n});\n```\n\nThe client automatically retries on 429 (rate limit) and 5xx errors with exponential backoff and `Retry-After` header support.\n\n### Custom Logger\n\n```typescript\nimport { silentLogger } from \"@alterlab/sdk\";\n\n// Silent mode (no console output)\nconst client = new AlterLab({ apiKey: \"...\", logger: silentLogger });\n\n// Custom logger\nconst client = new AlterLab({\n  apiKey: \"...\",\n  logger: {\n    debug: (msg) => myLogger.debug(msg),\n    warn: (msg) => myLogger.warn(msg),\n    error: (msg) => myLogger.error(msg),\n  },\n});\n```\n\n## TypeScript\n\nThe SDK exports full TypeScript types for all requests and responses:\n\n```typescript\nimport type {\n  ScrapeRequest,\n  ScrapeResponse,\n  CrawlRequest,\n  CrawlResponse,\n  CrawlStatusResponse,\n  BatchScrapeRequest,\n  BatchResponse,\n  CostEstimateRequest,\n  CostEstimateResponse,\n  JobStatusResponse,\n  UsageStats,\n  AlterLabOptions,\n  WaitForJobOptions,\n  CrawlAndWaitOptions,\n} from \"@alterlab/sdk\";\n```\n\n## Error Handling\n\n```typescript\ntry {\n  const result = await client.scrape({ url: \"https://example.com\" });\n} catch (error) {\n  if (axios.isAxiosError(error)) {\n    console.log(`HTTP ${error.response?.status}: ${error.response?.data}`);\n  } else {\n    console.log(`Error: ${error.message}`);\n  }\n}\n```\n\nErrors from the API include the HTTP status code and detail message in the Axios error response. The SDK retries transient errors (429, 5xx) automatically.\n\n## Links\n\n- [Dashboard](https://alterlab.io/dashboard) — Get your API key\n- [API Documentation](https://alterlab.io/docs) — Full API reference\n- [PyPI package](https://pypi.org/project/alterlab/) — Python SDK\n","readmeFilename":"README.md"}