@captapi/sdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,73 +1,73 @@
1
- # @captapi/sdk
2
-
3
- Official typed TypeScript SDK for the [Captapi](https://captapi.com) social media API — 178 endpoints across 32 platforms (YouTube, TikTok, Instagram, Facebook, X/Twitter, Reddit, Threads, Bluesky, Pinterest, LinkedIn, Twitch, Spotify, SoundCloud, GitHub, and more).
4
-
5
- - Zero dependencies — built on native `fetch` (Node 18+, Deno, Bun, edge runtimes)
6
- - Full type definitions for every endpoint and parameter — great autocomplete for humans and AI coding agents
7
- - Errors always throw a `CaptapiError` with status, code, and detail — no silent failures
8
- - One bounded retry on 502/503 (skipped when the API says the failure is persistent or already cached)
9
-
10
- ## Install
11
-
12
- ```bash
13
- npm install @captapi/sdk
14
- ```
15
-
16
- ## Quick start
17
-
18
- ```ts
19
- import { Captapi } from "@captapi/sdk";
20
-
21
- const client = new Captapi({ apiKey: "YOUR_KEY" }); // or set CAPTAPI_API_KEY
22
-
23
- const transcript = await client.youtube.transcript({
24
- url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
25
- });
26
- console.log(transcript.data);
27
-
28
- const profile = await client.tiktok.channelDetails({ url: "https://tiktok.com/@nba" });
29
- const posts = await client.reddit.subredditPosts({ url: "https://reddit.com/r/programming", limit: 25 });
30
- const ads = await client.googleAdLibrary.companyAds({ advertiser: "nike.com" });
31
- ```
32
-
33
- ## Error handling
34
-
35
- Every failed request throws — nothing fails silently:
36
-
37
- ```ts
38
- import { Captapi, CaptapiError } from "@captapi/sdk";
39
-
40
- try {
41
- await client.instagram.channelDetails({ url: "https://instagram.com/whoever" });
42
- } catch (err) {
43
- if (err instanceof CaptapiError) {
44
- console.error(err.status, err.code, err.detail);
45
- }
46
- }
47
- ```
48
-
49
- ## Options
50
-
51
- ```ts
52
- new Captapi({
53
- apiKey: "...", // default: process.env.CAPTAPI_API_KEY
54
- baseUrl: "https://api.captapi.com", // default
55
- timeoutMs: 120_000, // default
56
- });
57
- ```
58
-
59
- ## Responses
60
-
61
- All methods resolve to the standard Captapi envelope:
62
-
63
- ```json
64
- { "success": true, "data": { ... } }
65
- ```
66
-
67
- ## Links
68
-
69
- - [Documentation](https://captapi.com/docs)
70
- - [Get an API key (100 free credits)](https://captapi.com/dashboard/api-keys)
71
- - Python SDK: [`pip install captapi`](https://pypi.org/project/captapi/)
72
- - MCP server for AI agents: [`@captapi/mcp`](https://www.npmjs.com/package/@captapi/mcp)
73
- - CLI: [`@captapi/cli`](https://www.npmjs.com/package/@captapi/cli)
1
+ # @captapi/sdk
2
+
3
+ Official typed TypeScript SDK for the [Captapi](https://captapi.com) social media API — 178 endpoints across 32 platforms (YouTube, TikTok, Instagram, Facebook, X/Twitter, Reddit, Threads, Bluesky, Pinterest, LinkedIn, Twitch, Spotify, SoundCloud, GitHub, and more).
4
+
5
+ - Zero dependencies — built on native `fetch` (Node 18+, Deno, Bun, edge runtimes)
6
+ - Full type definitions for every endpoint and parameter — great autocomplete for humans and AI coding agents
7
+ - Errors always throw a `CaptapiError` with status, code, and detail — no silent failures
8
+ - One bounded retry on 502/503 (skipped when the API says the failure is persistent or already cached)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @captapi/sdk
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```ts
19
+ import { Captapi } from "@captapi/sdk";
20
+
21
+ const client = new Captapi({ apiKey: "YOUR_KEY" }); // or set CAPTAPI_API_KEY
22
+
23
+ const transcript = await client.youtube.transcript({
24
+ url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
25
+ });
26
+ console.log(transcript.data);
27
+
28
+ const profile = await client.tiktok.channelDetails({ url: "https://tiktok.com/@nba" });
29
+ const posts = await client.reddit.subredditPosts({ url: "https://reddit.com/r/programming", limit: 25 });
30
+ const ads = await client.googleAdLibrary.companyAds({ advertiser: "nike.com" });
31
+ ```
32
+
33
+ ## Error handling
34
+
35
+ Every failed request throws — nothing fails silently:
36
+
37
+ ```ts
38
+ import { Captapi, CaptapiError } from "@captapi/sdk";
39
+
40
+ try {
41
+ await client.instagram.channelDetails({ url: "https://instagram.com/whoever" });
42
+ } catch (err) {
43
+ if (err instanceof CaptapiError) {
44
+ console.error(err.status, err.code, err.detail);
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Options
50
+
51
+ ```ts
52
+ new Captapi({
53
+ apiKey: "...", // default: process.env.CAPTAPI_API_KEY
54
+ baseUrl: "https://api.captapi.com", // default
55
+ timeoutMs: 120_000, // default
56
+ });
57
+ ```
58
+
59
+ ## Responses
60
+
61
+ All methods resolve to the standard Captapi envelope:
62
+
63
+ ```json
64
+ { "success": true, "data": { ... } }
65
+ ```
66
+
67
+ ## Links
68
+
69
+ - [Documentation](https://captapi.com/docs)
70
+ - [Get an API key (100 free credits)](https://captapi.com/dashboard/api-keys)
71
+ - Python SDK: [`pip install captapi`](https://pypi.org/project/captapi/)
72
+ - MCP server for AI agents: [`@captapi/mcp`](https://www.npmjs.com/package/@captapi/mcp)
73
+ - CLI: [`@captapi/cli`](https://www.npmjs.com/package/@captapi/cli)
package/dist/core.js CHANGED
@@ -15,18 +15,24 @@ export class CaptapiError extends Error {
15
15
  }
16
16
  const RETRY_STATUSES = new Set([502, 503]);
17
17
  const RETRY_DELAY_MS = 500;
18
+ function errorBag(body) {
19
+ if (!body || typeof body !== "object")
20
+ return null;
21
+ const root = body;
22
+ if (root.error && typeof root.error === "object") {
23
+ return root.error;
24
+ }
25
+ if (root.detail && typeof root.detail === "object") {
26
+ return root.detail;
27
+ }
28
+ return root;
29
+ }
18
30
  function shouldRetry(status, body) {
19
- if (!RETRY_STATUSES.has(status))
20
- return false;
21
- const bag = body && typeof body === "object" && "detail" in body
22
- ? body.detail
23
- : body;
24
- if (bag && typeof bag === "object") {
25
- const flags = bag;
26
- if (flags.persistent === true || flags.replayed === true)
27
- return false;
31
+ const bag = errorBag(body);
32
+ if (bag && typeof bag.retryable === "boolean") {
33
+ return bag.retryable === true;
28
34
  }
29
- return true;
35
+ return RETRY_STATUSES.has(status);
30
36
  }
31
37
  export class HttpCore {
32
38
  apiKey;
@@ -45,13 +51,19 @@ export class HttpCore {
45
51
  this.fetchImpl = options.fetch ?? globalThis.fetch;
46
52
  }
47
53
  fail(path, status, body) {
48
- const detail = body?.detail ?? body?.error ?? body;
49
- const code = typeof body?.detail === "object" && body?.detail?.error
50
- ? String(body.detail.error)
54
+ const bag = errorBag(body);
55
+ const detail = bag ?? body?.detail ?? body?.error ?? body;
56
+ const code = typeof bag?.code === "string"
57
+ ? bag.code
51
58
  : typeof body?.error === "string"
52
59
  ? body.error
53
60
  : undefined;
54
- return new CaptapiError(`Captapi ${path} returned ${status}: ${typeof detail === "string" ? detail : JSON.stringify(detail ?? "unknown error")}`, status, code, detail);
61
+ const message = typeof bag?.message === "string"
62
+ ? bag.message
63
+ : typeof detail === "string"
64
+ ? detail
65
+ : JSON.stringify(detail ?? "unknown error");
66
+ return new CaptapiError(`Captapi ${path} returned ${status}: ${message}`, status, code, detail);
55
67
  }
56
68
  async parseBody(res) {
57
69
  try {