apick-api 4.3.1 → 4.5.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/docs/guide.en.md CHANGED
@@ -1,410 +1,413 @@
1
- # apick-api English guide
2
-
3
- ## Skill performance report
4
-
5
- Use `const { data } = await apick.getSkillPerformance()` to read the [public measurements](https://apick.app/skills/performance). Review sample sizes, dates, methods and failure reasons together. Model scores are not objective accuracy; unmeasured values are `null`. Reading the report does not execute a paid Skill.
6
-
7
- ## Request and response formats
8
-
9
- All SDK requests with a body use `multipart/form-data`. Arrays use separate indexed fields such as `utterance_ids[0]`; let the SDK set the Content-Type boundary. GET requests have no body. The server continues accepting older JSON requests for compatibility.
10
-
11
- The SDK preserves original LF/CR characters using UTF-8 Base64 and a `__apick_encoding[field]=base64-utf8` form metadata field. Excel cells also carry type metadata; dates become ISO strings and sparse array cells become null.
12
-
13
- Responses remain service-specific JSON or direct files. A failed download may return JSON, which the SDK exposes as `ApickApiError`. Individual REST guides provide OpenAPI and Postman downloads. External MCP connections retain JSON-RPC.
14
-
15
- `apick-api` is the official zero-dependency Node.js SDK for a focused set of popular APICK REST APIs. It supports ESM, CommonJS, and TypeScript.
16
-
17
- ## Install and authenticate
18
-
19
- ```bash
20
- npm install apick-api
21
- ```
22
-
23
- ```js
24
- import { ApickClient } from 'apick-api';
25
-
26
- const client = new ApickClient({
27
- apiKey: process.env.APICK_API_KEY,
28
- timeoutMs: 60_000
29
- });
30
- ```
31
-
32
- Pass the API key only to the constructor. The SDK does not keep it in enumerable client properties and never prints it in logs or error messages.
33
-
34
- Leave the allowed-IP list blank for unrestricted access. To restrict access, register the public IPv4 address seen by APICK as an exact address or CIDR such as `/32`. Changes apply immediately with no separate synchronization.
35
-
36
- ## Business, validation, and addresses
37
-
38
- ```js
39
- const business = await client.businessDetails('439-87-00761');
40
- const venture = await client.ventureBusiness('4398700761');
41
- const email = await client.validateEmail('sample@example.com');
42
- const phone = await client.validatePhone('01012341234');
43
- const holidays = await client.holidays(2026, 10);
44
- const addresses = await client.searchAddress('가산디지털로', { page: 1 });
45
- ```
46
-
47
- Hyphens are removed from business numbers automatically. Invalid required values fail locally with `TypeError` or `RangeError` before an API request is sent.
48
-
49
- ## Parcel tracking
50
-
51
- Use carrier-specific tracking when you know the carrier:
52
-
53
- ```js
54
- const parcel = await client.trackParcel('cj', '123456789012');
55
- ```
56
-
57
- Use automatic carrier detection when you only have the tracking number:
58
-
59
- ```js
60
- const parcel = await client.trackParcelAuto('123456789012');
61
- ```
62
-
63
- ## Domain and search tools
64
-
65
- ```js
66
- const dns = await client.dnsLookup('apick.app');
67
- const location = await client.geolocate('apick.app');
68
- const registration = await client.whois('apick.app');
69
- const web = await client.googleSearch('APICK API', { page: 1 });
70
- const images = await client.googleImageSearch('Seoul skyline', { page: 1 }); // pages 1-5
71
- const news = await client.googleNewsSearch('semiconductor', { page: 1 });
72
- const shopping = await client.googleShoppingSearch('wireless earbuds');
73
- const places = await client.googleMapsSearch('Gangnam cafe'); // usually 30-60 seconds
74
- const rank = await client.googleRankCheck('address search API', 'apick.app'); // top 100
75
- ```
76
-
77
- ## Public social profiles
78
-
79
- Only public information from public accounts is returned. Lookups usually take 20-60 seconds, so allow a request timeout of at least 100 seconds. Failed calls are not charged.
80
-
81
- ```js
82
- const instagram = await client.instagramProfile('natgeo');
83
- const post = await client.instagramPost('https://www.instagram.com/p/DeHufcWDCjf/');
84
- const tiktok = await client.tiktokProfile('https://www.tiktok.com/@tiktok');
85
- const product = await client.amazonProduct('B0BDHWDR12');
86
- const x = await client.xProfile('NASA');
87
- ```
88
-
89
- ### Collection jobs (lists, comments, search, reviews)
90
-
91
- Products that collect many items return a `job_id` immediately. Submitting reserves `maxResults` × unit price; when you read the result, only the results actually returned are charged and the rest is refunded. Failed jobs and missing targets are fully refunded, and results are kept for 72 hours. Comment and review authors are public usernames only.
92
-
93
- ```js
94
- const job = await client.createTiktokSearchJob('camping recipes', { maxResults: 10, idempotencyKey: 'camping-0001' });
95
- const done = await client.waitForScrapeJob(job.data.job_id); // every 10 s, up to 10 min
96
- console.log(done.data.status, done.data.charged_point, done.data.items);
97
- ```
98
-
99
- Result-billed collection jobs are charged per result; from 2026-11-06 a job with at least one result has a base fee (10 points, Instagram comments 50). Jobs with no results or failures are fully refunded.
100
-
101
- For a Google Maps place, pass the `place_id` from `googleMapsSearch` to get details, the star distribution and top reviews (up to 10). 20 points per place; text is returned in English.
102
-
103
- ```js
104
- const places = await client.googleMapsSearch('cafes near Gangnam station');
105
- const placeJob = await client.createGoogleMapsPlaceJob(places.data.items[0].place_id);
106
- const place = await client.waitForScrapeJob(placeJob.data.job_id);
107
- console.log(place.data.items[0].rating_distribution, place.data.items[0].top_reviews);
108
- ```
109
-
110
- ## OCR
111
-
112
- OCR accepts PNG and JPEG files up to 50MB.
113
-
114
- ```js
115
- const ocr = await client.ocr('./receipt.jpg');
116
- console.log(ocr.data.result.full_text);
117
- ```
118
-
119
- For in-memory input, supply a filename and MIME type when needed:
120
-
121
- ```js
122
- await client.ocr(bytes, {
123
- filename: 'scan.png',
124
- contentType: 'image/png'
125
- });
126
- ```
127
-
128
- ## Generated files
129
-
130
- Query `listGeminiTtsVoices()` and `listOpenAiTtsVoices()` for current voices; legacy `TTS_VOICE_IDS` are retired.
131
-
132
-
133
- Normalization is on by default and adds a skill charge. Set `normalize_text: false` (SDK `normalizeText: false`) to skip execution and billing. The skill price is its base fee (seller amount and base commission) plus actual AI cost × the pinned exchange rate × 1.4. Normalization failure stops synthesis; final server/provider failures refund normalization as well. ASS preserves the original spelling.
134
-
135
- ```js
136
- const screenshot = await client.screenshot('https://example.com');
137
- await screenshot.save('./example.jpeg');
138
-
139
- // Public YouTube videos: a video URL or the 11-character video ID
140
- const video = await client.youtubeMetadata('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
141
- const tracks = await client.youtubeSubtitleList(video.data.video_id);
142
- // Auto-translated captions (translated=true) may be refused by YouTube, so pick a manual or original-language track.
143
- const track = tracks.data.subtitles[0] || tracks.data.automatic_captions.find(item => !item.translated);
144
- const subtitle = await client.youtubeSubtitle(video.data.video_id, track.lang, { format: 'srt' });
145
- await subtitle.save('./' + subtitle.filename);
146
- await (await client.youtubeThumbnail(video.data.video_id)).save('./thumbnail.jpg');
147
-
148
- // Search, channel, comments; check formats, then download (base fee + per-10MB fee, 1-hour link)
149
- const found = await client.youtubeSearch('파이썬 기초 강좌', { count: 5, sort: 'views' });
150
- const channel = await client.youtubeChannel('@jocoding', { tab: 'videos', count: 10 });
151
- const comments = await client.youtubeComments(found.data.results[0].id, { count: 20, sort: 'top' });
152
- const formats = await client.youtubeFormats(found.data.results[0].id);
153
- const mp4 = await client.downloadYoutubeVideo(found.data.results[0].id, { quality: '720' });
154
- console.log(mp4.data.download_url, mp4.data.billing.total);
155
- const mp3 = await client.downloadYoutubeAudio(found.data.results[0].id, { format: 'mp3', bitrate: 192, start: '0:30', end: '1:30' });
156
-
157
- const created = await client.createTtsJob('오늘의 이야기를 시작합니다.', { voiceId: 'Kore' });
158
- const jobId = created.data.job_id;
159
- let job = await client.getTtsJob(jobId);
160
- while (job.data.status === 'waiting' || job.data.status === 'processing') {
161
- await new Promise(resolve => setTimeout(resolve, 3000));
162
- job = await client.getTtsJob(jobId);
163
- }
164
- if (job.data.status === 'completed') {
165
- const result = await client.downloadTtsResult(jobId);
166
- await result.save(`./${jobId}.mp3`); // audio/mpeg; downloadable once
167
- const subtitles = await client.downloadTtsSubtitles(jobId);
168
- await subtitles.save(`./${jobId}.ass`); // ASS subtitles; separately downloadable once
169
- }
170
-
171
- // Cancellation is available while waiting or processing and does not refund the accepted charge.
172
- // MP3 and ASS downloads each consume their server copy immediately and cannot be repeated.
173
-
174
- const pdf = await client.htmlToPdf('<h1>Report</h1>', { pagination: true });
175
- await pdf.save('./report.pdf');
176
-
177
- const excel = await client.jsonToExcel([{ item: 'A', count: 3 }], {
178
- sheetName: 'Inventory'
179
- });
180
- await excel.save('./inventory.xlsx');
181
- ```
182
-
183
- Binary results expose `bytes`, `size`, `filename`, `contentType`, and `meta`. `save()` writes a file in Node.js, while `toBlob()` creates a standard `Blob`.
184
-
185
- ## Text AI
186
-
187
- ```js
188
- const summary = await client.summarize(longText);
189
- const polished = await client.polish(draftText);
190
- ```
191
-
192
- Text input is limited to 100,000 characters.
193
-
194
- ## Image AI
195
-
196
- Images use a fixed per-image price by `quality`: `basic` (default) 40 points, `advanced` 350 points and `premium` 1,400 points, regardless of size. Repeated requests are never reused; each request is generated and charged again. The total is deducted when a request is accepted and failed images are refunded. If a job accepted under the previous pricing returns a settlement-pending response (HTTP 202, `billing_status: "pending"`), poll `getImageJob(job_id)`.
197
-
198
- ```js
199
- const result = await client.generateImages('A clean product photo on white', {
200
- imageCount: 4, size: '1024x1024', outputFormat: 'webp', quality: 'advanced'
201
- });
202
-
203
- const referenceResult = await client.generateImages('Keep the product shape and composition, and change the background to a sunny kitchen', {
204
- referenceImage: './reference.png',
205
- referenceFilename: 'reference.png',
206
- referenceContentType: 'image/png'
207
- });
208
-
209
- const job = await client.createImageGenerationJob('Landscape article cover concepts', { imageCount: 20, size: '1536x1024' });
210
- const status = await client.getImageJob(job.data.job_id);
211
- ```
212
-
213
- `imageCount` is the number of images to make and defaults to one. Synchronous generation and editing support 1–4 images; job methods support 1–50. Add `referenceImage` to generation when the prompt should build from an existing composition, palette, or product shape. Editing accepts one PNG, JPEG, or WebP source up to 50 MB plus a prompt; mask files are not supported. Choose one of five sizes: `1024x1024`, `1536x1024`, `1024x1536`, `1152x864`, or `864x1152`; prompts may contain up to 28,000 characters. The full image count × 25 points is deducted when accepted, and 25 points are refunded immediately for every failed image. Accepted jobs cannot be cancelled. Results remain available for 24 hours.
214
-
215
- `idempotencyKey` is a safety identifier that prevents duplicate generation and billing if a network problem sends the same request twice. Use 8–128 letters, numbers, underscores, or hyphens. Reuse it only for the exact same request and create a new value when the prompt or options change.
216
-
217
- Methods: `generateImages`, `editImages`, `createImageGenerationJob`, `createImageEditJob`, `getImageJob`, `downloadImageJobImage`, and `downloadImageJobArchive`.
218
-
219
- ## Response shape
220
-
221
- JSON methods resolve to:
222
-
223
- ```ts
224
- {
225
- data: unknown;
226
- meta: {
227
- cost: number | null;
228
- durationMs: number | null;
229
- };
230
- }
231
- ```
232
-
233
- `meta.cost` is the point charge reported by the API response. See the APICK documentation for current rates.
234
-
235
- ## Identity masking
236
-
237
- ```js
238
- const png = await client.maskResidentNumber('./id-card.jpg', { type: 3 });
239
- await png.save('./masked.png');
240
-
241
- const result = await client.maskDriverLicense('./license.jpg');
242
- console.log(result.data.result.fields);
243
- ```
244
-
245
- The document-specific methods are `maskResidenceCard`, `maskPassport`, `maskIdCard`, and `maskDriverLicense`. Identity errors are exposed as `IDENTITY_TEXT_UNREADABLE`, `IDENTITY_DOCUMENT_MISMATCH`, or `IDENTITY_PROCESSING_FAILED` through `ApickApiError.serviceCode`.
246
-
247
- `maskResidenceCard` accepts one front-side image of a residence card, permanent resident card, or overseas Korean resident card. Permanent and overseas Korean card support is limited to PII masking and does not expand the alien registration card authenticity-check scope.
248
-
249
- ## Simple-auth data lookups
250
-
251
- Employment, income, pension, driver's license, health checkup, cash receipt deduction, and tax return history lookups require the user's own simple-auth verification, so the call is split into acceptance (`request*`) and result polling (`get*`).
252
-
253
- Use this helper for all seven products. Poll sequentially with the same `transactionId`, waiting 5→10→20→30 seconds and then keeping the 30-second interval. Return the result immediately when `resultAvailable === true`. `SUCCESS` means full success; `PARTIAL_SUCCESS` means partial success, so inspect `sources` for missing or failed items. `AUTH_REJECTED`, `AUTH_EXPIRED`, and `FAILED` are terminal failures; `errorCode: 'RESULT_EXPIRED'` means the retained result has expired. Never resubmit automatically after failure or expiry.
254
-
255
- <!-- simple-auth-polling:start -->
256
- ```js
257
- async function pollDataResult(getResult, accepted, { timeoutMs = 600_000 } = {}) {
258
- const pending = new Set([
259
- 'AUTH_REQUESTED', 'AUTH_WAITING', 'AUTH_COMPLETED', 'COLLECTING', 'COLLECTED'
260
- ]);
261
- const failed = new Set(['AUTH_REJECTED', 'AUTH_EXPIRED', 'FAILED']);
262
- const completed = new Set(['SUCCESS', 'PARTIAL_SUCCESS']);
263
- const delays = [5_000, 10_000, 20_000, 30_000];
264
- const deadline = Date.now() + timeoutMs;
265
- const transactionId = accepted.data.transactionId;
266
- const stop = code => { throw Object.assign(new Error(code), { code }); };
267
- let response = accepted;
268
- let attempt = 0;
269
-
270
- for (;;) {
271
- const data = response.data;
272
- if (data.errorCode === 'RESULT_EXPIRED') stop('RESULT_EXPIRED');
273
- if (failed.has(data.status)) stop(data.errorCode || data.status);
274
- if (data.resultAvailable === true) {
275
- if (data.result == null) stop('INVALID_RESULT');
276
- return response;
277
- }
278
- if (completed.has(data.status)) stop('RESULT_NOT_AVAILABLE');
279
- if (!pending.has(data.status)) stop('UNKNOWN_STATUS');
280
-
281
- const awaitingApproval = ['AUTH_REQUESTED', 'AUTH_WAITING'].includes(data.status);
282
- const authDeadline = Date.parse(data.expiresAt || accepted.data.expiresAt);
283
- const limit = awaitingApproval && Number.isFinite(authDeadline)
284
- ? Math.min(deadline, authDeadline) : deadline;
285
- const timeoutCode = awaitingApproval && limit === authDeadline
286
- ? 'AUTH_WAIT_TIMEOUT' : 'CLIENT_POLL_TIMEOUT';
287
- const remaining = limit - Date.now();
288
- if (remaining <= 0) stop(timeoutCode);
289
- await new Promise(resolve => setTimeout(resolve,
290
- Math.min(delays[Math.min(attempt++, delays.length - 1)], remaining)));
291
- if (Date.now() >= limit) stop(timeoutCode);
292
- response = await getResult(transactionId);
293
- }
294
- }
295
- ```
296
- <!-- simple-auth-polling:end -->
297
-
298
- `AUTH_REQUESTED` and `AUTH_WAITING` wait for the user's approval; `AUTH_COMPLETED`, `COLLECTING`, and `COLLECTED` keep polling until a result is available. Billing fields (`charged`, `success`) do not indicate completion. Apply `expiresAt` only while awaiting approval, then use the overall waiting limit. The example's 10-minute limit is a client policy; configure the SDK's `timeoutMs` separately to bound each in-flight call. `AUTH_WAIT_TIMEOUT`, `CLIENT_POLL_TIMEOUT`, `RESULT_NOT_AVAILABLE`, `INVALID_RESULT`, and `UNKNOWN_STATUS` are local example errors that prevent endless polling on inconsistent responses or unknown states. Transport errors propagate without retries.
299
-
300
- <!-- simple-auth-usage:start -->
301
- ```js
302
- const accepted = await client.requestDrivingLicense({
303
- name: 'Hong Gildong',
304
- birthDate: '19900101',
305
- phone: '01011112222',
306
- authProvider: 'kakao'
307
- });
308
-
309
- try {
310
- const result = await pollDataResult(id => client.getDrivingLicense(id), accepted);
311
- if (result.data.status === 'PARTIAL_SUCCESS') console.warn(result.data.sources);
312
- console.log(result.data.result);
313
- } catch (error) {
314
- if ((error.serviceCode || error.code) === 'RESULT_EXPIRED') {
315
- console.error('Result expired. Ask the user before starting a new authentication request.');
316
- } else {
317
- throw error;
318
- }
319
- }
320
- ```
321
- <!-- simple-auth-usage:end -->
322
-
323
- Sources: [APICK development guide](https://apick.app/dev_guide/data_health_checkup) · [MCP 3.5.0 status contract](https://github.com/lead788/apick-mcp/blob/a803abcb81d07377c85f49bb0b670baf0c17ed04/TOOLS.md)
324
-
325
- `authProvider` is one of the 13 values in `AUTH_PROVIDERS` (kakao, naver, toss, pass, samsung, kb, shinhan, hana, woori, ibk, nh, kakaobank, banksalad). Acceptance is billed at a flat rate; the result is billed only on its first return and free to re-poll afterward. `requestEmployment` takes an optional `insuranceYears` (1-3), `requestPersonalIncome` takes `incomeYears` (1-5), and `requestNpsJoinHistory` takes optional `from`/`to` (`YYYY-MM`). `requestCashReceiptDeduction` takes optional `incomeYears` (1-3) and `requestTaxReturnHistory` takes optional `years` (1-10). The remaining products are `requestDrivingLicense` and `requestHealthCheckup`.
326
-
327
- ## Skills
328
-
329
- Search and run reviewed Skills. Each Skill fixes its input format (`input_schema`), output format (`output_schema`) and base amount (`price_points`). A run is charged only when a result in the promised format is returned.
330
-
331
- For Skills that use generative AI (`usage_priced: true`) the actual usage of each run is added to the base amount, so the charge varies per run. `quoteSkill()` returns the estimated amount (`estimated_points`) and the maximum amount (`max_points`). The maximum is reserved when the run is accepted; when it finishes only the actual amount is charged and the rest is returned. Read the actual charge from `run.billing.charged_points`.
332
-
333
- ```js
334
- import { randomUUID } from 'node:crypto';
335
-
336
- const found = await client.searchSkills({ query: 'product name', limit: 5 });
337
- const skillId = found.data.items[0].skill_id;
338
- const detail = await client.getSkill(skillId);
339
- const input = { product_name: 'Sturdy folding umbrella' };
340
-
341
- const quote = await client.quoteSkill(skillId, input); // free, valid for 5 minutes
342
- const idempotencyKey = randomUUID(); // reuse the same value when retrying
343
- let run = (await client.runSkill(skillId, input, {
344
- idempotencyKey, quoteId: quote.data.quote_id, maxCostPoints: quote.data.max_points
345
- })).data;
346
- while (!['succeeded', 'failed', 'timed_out', 'cancelled'].includes(run.status)) {
347
- await new Promise(resolve => setTimeout(resolve, 2000));
348
- run = (await client.getSkillRun(run.run_id)).data;
349
- }
350
- console.log(run.status, run.billing, run.result);
351
- ```
352
-
353
- | Method | Description |
354
- | --- | --- |
355
- | `searchSkills({ query, category, sort, cursor, limit })` | Search. `limit` 1-20; `category` is one of `SKILL_CATEGORIES`, `sort` one of `SKILL_SORTS` |
356
- | `getSkill(skillId)` | Input and output formats, price, limits, examples |
357
- | `quoteSkill(skillId, input, { version })` | Validates the input and reports the points to be charged. Does not run |
358
- | `runSkill(skillId, input, { idempotencyKey, quoteId, maxCostPoints, version, waitSeconds })` | Run. `idempotencyKey` is required; `waitSeconds` 0-20 (default 20) |
359
- | `getSkillRun(runId)` / `getSkillRunResult(runId)` | Run status (with the result once succeeded) / result only. Results are kept for 7 days |
360
- | `cancelSkillRun(runId)` | Cancel an unfinished run. Cancelled runs are not charged |
361
- | `skillUsage({ cursor, limit })` | Your runs. `limit` 1-50 |
362
-
363
- - `sort` is one of `recommended`, `popular`, `used` (most run), `likes`, `rating`, `new`, `mine` (Skills your account runs most) and `liked` (Skills your account liked). Omit it for registration order.
364
- - Search and detail responses include `usage_label` (a usage tier such as `1,000회 미만` (under 1,000), `1,000+`, `1만+` (10,000+), not an exact count), `like_count`, `review_count` and `rating_average` (1-5, `null` without reviews). Likes and reviews are left on the Skill page of the APICK website.
365
- - `sort` is one of `recommended`, `popular`, `used` (most run), `likes`, `rating`, `new`, `mine` (Skills your account runs most) and `liked` (Skills your account liked). Omit it for registration order.
366
- - Search and detail responses include `usage_label` (a usage tier such as `1,000회 미만` (under 1,000), `1,000+`, `1만+` (10,000+), not an exact count), `like_count`, `review_count` and `rating_average` (1-5, `null` without reviews). Likes and reviews are left on the Skill page of the APICK website.
367
- - Skills responses are returned as-is in `data`. Read the charge from `data.billing.status` (`reserved`, `captured`, `released`, `partially_refunded`, `refunded`); `meta.cost` is not populated.
368
- - If a run does not finish within 20 seconds it comes back as `queued` or `running`; poll `getSkillRun`.
369
- - If a response is lost, call again with the same `idempotencyKey`. Points are charged once. The same key with a different input is rejected with `IDEMPOTENCY_CONFLICT`.
370
- - Rejections before acceptance throw `ApickApiError` (`serviceCode` such as `INVALID_INPUT`, `INSUFFICIENT_POINTS`, `PAYMENT_REQUIRED`, `PRICE_EXCEEDS_LIMIT`, `RATE_LIMITED`; extra information in `details`). Failures after acceptance are reported through `status` and `failure_code` (`EXECUTION_FAILED`, `OUTPUT_INVALID`, `TIMED_OUT`, `CANCELLED`, `UPSTREAM_UNAVAILABLE`).
371
- - Skills with `uses_generative_ai: true` produce results with generative AI. Verify them before relying on them.
372
-
373
- ## Errors and retries
374
-
375
- `ApickApiError` includes public error information: `code`, optional `serviceCode`, `status`, and `message`. The SDK does not retry automatically because a retry could duplicate an API call and its charge. If your application needs retries, decide explicitly after checking the error code and whether the operation is safe to repeat.
376
- ## TTS 이전 버전에서 전환 / TTS migration
377
-
378
- 기존 APICK 목소리와 자동 전환은 종료되었습니다. `createTtsJob`은 Gemini 기본 목소리 `Kore`를 사용합니다. `getTtsQuality`, `retryTtsJob`, `downloadTtsCandidate`의 기존 경로는 HTTP 409를 반환합니다.
379
-
380
- Legacy APICK voices and automatic fallback have retired. `createTtsJob` now defaults to Gemini voice `Kore`. The legacy quality, retry and candidate endpoints return HTTP 409.
381
-
382
- ## Video model versions
383
-
384
- Omitting `version` preserves Seedance 2.5, Veo 3.1 and Kling 3.0. Set `version` and `tier` explicitly to select a generation; jobs are never silently switched to another version. Submission and status responses include `version`.
385
-
386
- Available generations: Seedance 1.0/1.5/2.0/2.5, including Seedance 2.0 Standard/Fast/Mini; Veo 3.1 (Standard/Fast/Lite); Kling 1.6/2.0/2.1/2.5/2.6/3.0/O1/O3. Veo 3.0 is unavailable. Seedance 2.0 Mini supports 480p/720p and 4–15 seconds. Modes, tiers, resolutions, durations, audio, file limits and prices vary by combination. See the [Seedance](https://apick.app/dev_guide/seedancejobs), [Veo](https://apick.app/dev_guide/veojobs) and [Kling](https://apick.app/dev_guide/klingjobs) version tables. Unsupported combinations are rejected before submission.
387
-
388
- Seedance reference mode accepts `referenceImages`, `referenceVideos`, and `referenceAudios` (MP3/WAV) when supported by the selected version.
389
-
390
- ```js
391
- const job = await client.createVideoJob("kling", "A boat crossing the sea", {
392
- version: "1.6", tier: "std", mode: "text", duration: 5, audio: false,
393
- idempotencyKey: "boat-video-0001"
394
- });
395
- const status = await client.getVideoJob("kling", job.data.job_id);
396
- if (status.data.status === "completed") {
397
- await (await client.downloadVideoResult("kling", job.data.job_id)).save("boat.mp4");
398
- }
399
- ```
400
-
401
- ## Skill artifacts and execution estimates
402
-
403
- Skill details and quotes provide estimated price and duration ranges and tool call counts. Check the maximum reservation before execution, then use getSkillArtifact(runId, fileId) to download files with authentication. See [Skill artifacts](skill-artifacts.md) for fields and examples.
404
-
405
- ## Product-specific data guides
406
-
407
- Product-specific authenticated-data guides describe returned fields, source institutions, use cases, and FAQs. An authentication request starts access to the selected product and returns a transactionId for result polling. Request and response contracts and authentication are unchanged. See the [shared authentication guide](https://apick.app/dev_guide/data) for the flow and error handling.
408
-
409
-
410
- TTS: [Gemini·ChatGPT·정규화 / engines and normalization](tts.md). `createOpenAiTtsJob`, `listOpenAiTtsVoices`, `getTtsOptions`를 포함한 최신 계약입니다.
1
+ # apick-api English guide
2
+
3
+ ## Skill performance report
4
+
5
+ Use `const { data } = await apick.getSkillPerformance()` to read the [public measurements](https://apick.app/skills/performance). Review sample sizes, dates, methods and failure reasons together. Model scores are not objective accuracy; unmeasured values are `null`. Reading the report does not execute a paid Skill.
6
+
7
+ ## Request and response formats
8
+
9
+ All SDK requests with a body use `multipart/form-data`. Arrays use separate indexed fields such as `utterance_ids[0]`; let the SDK set the Content-Type boundary. GET requests have no body. The server continues accepting older JSON requests for compatibility.
10
+
11
+ The SDK preserves original LF/CR characters using UTF-8 Base64 and a `__apick_encoding[field]=base64-utf8` form metadata field. Excel cells also carry type metadata; dates become ISO strings and sparse array cells become null.
12
+
13
+ Responses remain service-specific JSON or direct files. A failed download may return JSON, which the SDK exposes as `ApickApiError`. Individual REST guides provide OpenAPI and Postman downloads. External MCP connections retain JSON-RPC.
14
+
15
+ `apick-api` is the official zero-dependency Node.js SDK for a focused set of popular APICK REST APIs. It supports ESM, CommonJS, and TypeScript.
16
+
17
+ ## Install and authenticate
18
+
19
+ ```bash
20
+ npm install apick-api
21
+ ```
22
+
23
+ ```js
24
+ import { ApickClient } from 'apick-api';
25
+
26
+ const client = new ApickClient({
27
+ apiKey: process.env.APICK_API_KEY,
28
+ timeoutMs: 60_000
29
+ });
30
+ ```
31
+
32
+ Pass the API key only to the constructor. The SDK does not keep it in enumerable client properties and never prints it in logs or error messages.
33
+
34
+ Leave the allowed-IP list blank for unrestricted access. To restrict access, register the public IPv4 address seen by APICK as an exact address or CIDR such as `/32`. Changes apply immediately with no separate synchronization.
35
+
36
+ ## Business, validation, and addresses
37
+
38
+ ```js
39
+ const business = await client.businessDetails('439-87-00761');
40
+ const venture = await client.ventureBusiness('4398700761');
41
+ const email = await client.validateEmail('sample@example.com');
42
+ const phone = await client.validatePhone('01012341234');
43
+ const holidays = await client.holidays(2026, 10);
44
+ const addresses = await client.searchAddress('가산디지털로', { page: 1 });
45
+ ```
46
+
47
+ Hyphens are removed from business numbers automatically. Invalid required values fail locally with `TypeError` or `RangeError` before an API request is sent.
48
+
49
+ ## Parcel tracking
50
+
51
+ Use carrier-specific tracking when you know the carrier:
52
+
53
+ ```js
54
+ const parcel = await client.trackParcel('cj', '123456789012');
55
+ ```
56
+
57
+ Use automatic carrier detection when you only have the tracking number:
58
+
59
+ ```js
60
+ const parcel = await client.trackParcelAuto('123456789012');
61
+ ```
62
+
63
+ ## Domain and search tools
64
+
65
+ ```js
66
+ const dns = await client.dnsLookup('apick.app');
67
+ const location = await client.geolocate('apick.app');
68
+ const registration = await client.whois('apick.app');
69
+ const web = await client.googleSearch('APICK API', { page: 1 });
70
+ const images = await client.googleImageSearch('Seoul skyline', { page: 1 }); // pages 1-5
71
+ const news = await client.googleNewsSearch('semiconductor', { page: 1 });
72
+ const shopping = await client.googleShoppingSearch('wireless earbuds');
73
+ const places = await client.googleMapsSearch('Gangnam cafe'); // usually 30-60 seconds
74
+ const rank = await client.googleRankCheck('address search API', 'apick.app'); // top 100
75
+ ```
76
+
77
+ ## Public social profiles
78
+
79
+ Only public information from public accounts is returned. Lookups usually take 20-60 seconds, so allow a request timeout of at least 100 seconds. Failed calls are not charged.
80
+
81
+ ```js
82
+ const instagram = await client.instagramProfile('natgeo');
83
+ const post = await client.instagramPost('https://www.instagram.com/p/DeHufcWDCjf/');
84
+ const tiktok = await client.tiktokProfile('https://www.tiktok.com/@tiktok');
85
+ const product = await client.amazonProduct('B0BDHWDR12');
86
+ const x = await client.xProfile('NASA');
87
+ const coupang = await client.coupangProduct('https://www.coupang.com/vp/products/7812020679');
88
+ const naver = await client.naverShoppingProduct('https://brand.naver.com/pulmuonebynature/products/4995061380');
89
+ const profile = await client.linkedinProfile('https://www.linkedin.com/in/example/');
90
+ const company = await client.linkedinCompany('https://www.linkedin.com/company/example/');
91
+ const job = await client.linkedinJob('https://www.linkedin.com/jobs/view/4001234567/');
92
+ ```
93
+
94
+ Coupang accepts a product number or a product URL (`/vp/products/{id}`), and Naver accepts a brand store product URL (`brand.naver.com/{store}/products/{id}`). LinkedIn accepts profile (`/in/`), company (`/company/`) and job posting (`/jobs/view/`) URLs.
95
+
96
+ ### Collection jobs (lists, comments, search, reviews)
97
+
98
+ Products that collect many items return a `job_id` immediately. Submitting reserves `maxResults` × unit price; when you read the result, only the results actually returned are charged and the rest is refunded. Failed jobs and missing targets are fully refunded, and results are kept for 72 hours. Comment and review authors are public usernames only.
99
+
100
+ ```js
101
+ const job = await client.createTiktokSearchJob('camping recipes', { maxResults: 10, idempotencyKey: 'camping-0001' });
102
+ const done = await client.waitForScrapeJob(job.data.job_id); // every 10 s, up to 10 min
103
+ console.log(done.data.status, done.data.charged_point, done.data.items);
104
+ ```
105
+
106
+ Result-billed collection jobs are charged per result; from 2026-11-06 a job with at least one result has a base fee (10 points, Instagram comments 50). Jobs with no results or failures are fully refunded.
107
+
108
+ For a Google Maps place, pass the `place_id` from `googleMapsSearch` to get details, the star distribution and top reviews (up to 10). 20 points per place; text is returned in English.
109
+
110
+ ```js
111
+ const places = await client.googleMapsSearch('cafes near Gangnam station');
112
+ const placeJob = await client.createGoogleMapsPlaceJob(places.data.items[0].place_id);
113
+ const place = await client.waitForScrapeJob(placeJob.data.job_id);
114
+ console.log(place.data.items[0].rating_distribution, place.data.items[0].top_reviews);
115
+ ```
116
+
117
+ ## OCR
118
+
119
+ OCR accepts PNG and JPEG files up to 50MB.
120
+
121
+ ```js
122
+ const ocr = await client.ocr('./receipt.jpg');
123
+ console.log(ocr.data.result.full_text);
124
+ ```
125
+
126
+ For in-memory input, supply a filename and MIME type when needed:
127
+
128
+ ```js
129
+ await client.ocr(bytes, {
130
+ filename: 'scan.png',
131
+ contentType: 'image/png'
132
+ });
133
+ ```
134
+
135
+ ## Generated files
136
+
137
+ Query `listGeminiTtsVoices()` and `listOpenAiTtsVoices()` for current voices; legacy `TTS_VOICE_IDS` are retired.
138
+
139
+
140
+ Normalization is on by default and adds a skill charge. Set `normalize_text: false` (SDK `normalizeText: false`) to skip execution and billing. The skill price is its base fee (seller amount and base commission) plus actual AI cost × the pinned exchange rate × 1.4. Normalization failure stops synthesis; final server/provider failures refund normalization as well. ASS preserves the original spelling.
141
+
142
+ ```js
143
+ const screenshot = await client.screenshot('https://example.com');
144
+ await screenshot.save('./example.jpeg');
145
+
146
+ // Public YouTube videos: a video URL or the 11-character video ID
147
+ const video = await client.youtubeMetadata('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
148
+ const tracks = await client.youtubeSubtitleList(video.data.video_id);
149
+ // Auto-translated captions (translated=true) may be refused by YouTube, so pick a manual or original-language track.
150
+ const track = tracks.data.subtitles[0] || tracks.data.automatic_captions.find(item => !item.translated);
151
+ const subtitle = await client.youtubeSubtitle(video.data.video_id, track.lang, { format: 'srt' });
152
+ await subtitle.save('./' + subtitle.filename);
153
+ await (await client.youtubeThumbnail(video.data.video_id)).save('./thumbnail.jpg');
154
+
155
+ // Search, channel, comments
156
+ const found = await client.youtubeSearch('파이썬 기초 강좌', { count: 5, sort: 'views' });
157
+ const channel = await client.youtubeChannel('@jocoding', { tab: 'videos', count: 10 });
158
+ const comments = await client.youtubeComments(found.data.results[0].id, { count: 20, sort: 'top' });
159
+
160
+ const created = await client.createTtsJob('오늘의 이야기를 시작합니다.', { voiceId: 'Kore' });
161
+ const jobId = created.data.job_id;
162
+ let job = await client.getTtsJob(jobId);
163
+ while (job.data.status === 'waiting' || job.data.status === 'processing') {
164
+ await new Promise(resolve => setTimeout(resolve, 3000));
165
+ job = await client.getTtsJob(jobId);
166
+ }
167
+ if (job.data.status === 'completed') {
168
+ const result = await client.downloadTtsResult(jobId);
169
+ await result.save(`./${jobId}.mp3`); // audio/mpeg; downloadable once
170
+ const subtitles = await client.downloadTtsSubtitles(jobId);
171
+ await subtitles.save(`./${jobId}.ass`); // ASS subtitles; separately downloadable once
172
+ }
173
+
174
+ // Cancellation is available while waiting or processing and does not refund the accepted charge.
175
+ // MP3 and ASS downloads each consume their server copy immediately and cannot be repeated.
176
+
177
+ const pdf = await client.htmlToPdf('<h1>Report</h1>', { pagination: true });
178
+ await pdf.save('./report.pdf');
179
+
180
+ const excel = await client.jsonToExcel([{ item: 'A', count: 3 }], {
181
+ sheetName: 'Inventory'
182
+ });
183
+ await excel.save('./inventory.xlsx');
184
+ ```
185
+
186
+ Binary results expose `bytes`, `size`, `filename`, `contentType`, and `meta`. `save()` writes a file in Node.js, while `toBlob()` creates a standard `Blob`.
187
+
188
+ ## Text AI
189
+
190
+ ```js
191
+ const summary = await client.summarize(longText);
192
+ const polished = await client.polish(draftText);
193
+ ```
194
+
195
+ Text input is limited to 100,000 characters.
196
+
197
+ ## Image AI
198
+
199
+ Images use a fixed per-image price by `quality`: `basic` (default) 40 points, `advanced` 350 points and `premium` 1,400 points, regardless of size. Repeated requests are never reused; each request is generated and charged again. The total is deducted when a request is accepted and failed images are refunded. If a job accepted under the previous pricing returns a settlement-pending response (HTTP 202, `billing_status: "pending"`), poll `getImageJob(job_id)`.
200
+
201
+ ```js
202
+ const result = await client.generateImages('A clean product photo on white', {
203
+ imageCount: 4, size: '1024x1024', outputFormat: 'webp', quality: 'advanced'
204
+ });
205
+
206
+ const referenceResult = await client.generateImages('Keep the product shape and composition, and change the background to a sunny kitchen', {
207
+ referenceImage: './reference.png',
208
+ referenceFilename: 'reference.png',
209
+ referenceContentType: 'image/png'
210
+ });
211
+
212
+ const job = await client.createImageGenerationJob('Landscape article cover concepts', { imageCount: 20, size: '1536x1024' });
213
+ const status = await client.getImageJob(job.data.job_id);
214
+ ```
215
+
216
+ `imageCount` is the number of images to make and defaults to one. Synchronous generation and editing support 1–4 images; job methods support 1–50. Add `referenceImage` to generation when the prompt should build from an existing composition, palette, or product shape. Editing accepts one PNG, JPEG, or WebP source up to 50 MB plus a prompt; mask files are not supported. Choose one of five sizes: `1024x1024`, `1536x1024`, `1024x1536`, `1152x864`, or `864x1152`; prompts may contain up to 28,000 characters. The full image count × 25 points is deducted when accepted, and 25 points are refunded immediately for every failed image. Accepted jobs cannot be cancelled. Results remain available for 24 hours.
217
+
218
+ `idempotencyKey` is a safety identifier that prevents duplicate generation and billing if a network problem sends the same request twice. Use 8–128 letters, numbers, underscores, or hyphens. Reuse it only for the exact same request and create a new value when the prompt or options change.
219
+
220
+ Methods: `generateImages`, `editImages`, `createImageGenerationJob`, `createImageEditJob`, `getImageJob`, `downloadImageJobImage`, and `downloadImageJobArchive`.
221
+
222
+ ## Response shape
223
+
224
+ JSON methods resolve to:
225
+
226
+ ```ts
227
+ {
228
+ data: unknown;
229
+ meta: {
230
+ cost: number | null;
231
+ durationMs: number | null;
232
+ };
233
+ }
234
+ ```
235
+
236
+ `meta.cost` is the point charge reported by the API response. See the APICK documentation for current rates.
237
+
238
+ ## Identity masking
239
+
240
+ ```js
241
+ const png = await client.maskResidentNumber('./id-card.jpg', { type: 3 });
242
+ await png.save('./masked.png');
243
+
244
+ const result = await client.maskDriverLicense('./license.jpg');
245
+ console.log(result.data.result.fields);
246
+ ```
247
+
248
+ The document-specific methods are `maskResidenceCard`, `maskPassport`, `maskIdCard`, and `maskDriverLicense`. Identity errors are exposed as `IDENTITY_TEXT_UNREADABLE`, `IDENTITY_DOCUMENT_MISMATCH`, or `IDENTITY_PROCESSING_FAILED` through `ApickApiError.serviceCode`.
249
+
250
+ `maskResidenceCard` accepts one front-side image of a residence card, permanent resident card, or overseas Korean resident card. Permanent and overseas Korean card support is limited to PII masking and does not expand the alien registration card authenticity-check scope.
251
+
252
+ ## Simple-auth data lookups
253
+
254
+ Employment, income, pension, driver's license, health checkup, cash receipt deduction, and tax return history lookups require the user's own simple-auth verification, so the call is split into acceptance (`request*`) and result polling (`get*`).
255
+
256
+ Use this helper for all seven products. Poll sequentially with the same `transactionId`, waiting 5→10→20→30 seconds and then keeping the 30-second interval. Return the result immediately when `resultAvailable === true`. `SUCCESS` means full success; `PARTIAL_SUCCESS` means partial success, so inspect `sources` for missing or failed items. `AUTH_REJECTED`, `AUTH_EXPIRED`, and `FAILED` are terminal failures; `errorCode: 'RESULT_EXPIRED'` means the retained result has expired. Never resubmit automatically after failure or expiry.
257
+
258
+ <!-- simple-auth-polling:start -->
259
+ ```js
260
+ async function pollDataResult(getResult, accepted, { timeoutMs = 600_000 } = {}) {
261
+ const pending = new Set([
262
+ 'AUTH_REQUESTED', 'AUTH_WAITING', 'AUTH_COMPLETED', 'COLLECTING', 'COLLECTED'
263
+ ]);
264
+ const failed = new Set(['AUTH_REJECTED', 'AUTH_EXPIRED', 'FAILED']);
265
+ const completed = new Set(['SUCCESS', 'PARTIAL_SUCCESS']);
266
+ const delays = [5_000, 10_000, 20_000, 30_000];
267
+ const deadline = Date.now() + timeoutMs;
268
+ const transactionId = accepted.data.transactionId;
269
+ const stop = code => { throw Object.assign(new Error(code), { code }); };
270
+ let response = accepted;
271
+ let attempt = 0;
272
+
273
+ for (;;) {
274
+ const data = response.data;
275
+ if (data.errorCode === 'RESULT_EXPIRED') stop('RESULT_EXPIRED');
276
+ if (failed.has(data.status)) stop(data.errorCode || data.status);
277
+ if (data.resultAvailable === true) {
278
+ if (data.result == null) stop('INVALID_RESULT');
279
+ return response;
280
+ }
281
+ if (completed.has(data.status)) stop('RESULT_NOT_AVAILABLE');
282
+ if (!pending.has(data.status)) stop('UNKNOWN_STATUS');
283
+
284
+ const awaitingApproval = ['AUTH_REQUESTED', 'AUTH_WAITING'].includes(data.status);
285
+ const authDeadline = Date.parse(data.expiresAt || accepted.data.expiresAt);
286
+ const limit = awaitingApproval && Number.isFinite(authDeadline)
287
+ ? Math.min(deadline, authDeadline) : deadline;
288
+ const timeoutCode = awaitingApproval && limit === authDeadline
289
+ ? 'AUTH_WAIT_TIMEOUT' : 'CLIENT_POLL_TIMEOUT';
290
+ const remaining = limit - Date.now();
291
+ if (remaining <= 0) stop(timeoutCode);
292
+ await new Promise(resolve => setTimeout(resolve,
293
+ Math.min(delays[Math.min(attempt++, delays.length - 1)], remaining)));
294
+ if (Date.now() >= limit) stop(timeoutCode);
295
+ response = await getResult(transactionId);
296
+ }
297
+ }
298
+ ```
299
+ <!-- simple-auth-polling:end -->
300
+
301
+ `AUTH_REQUESTED` and `AUTH_WAITING` wait for the user's approval; `AUTH_COMPLETED`, `COLLECTING`, and `COLLECTED` keep polling until a result is available. Billing fields (`charged`, `success`) do not indicate completion. Apply `expiresAt` only while awaiting approval, then use the overall waiting limit. The example's 10-minute limit is a client policy; configure the SDK's `timeoutMs` separately to bound each in-flight call. `AUTH_WAIT_TIMEOUT`, `CLIENT_POLL_TIMEOUT`, `RESULT_NOT_AVAILABLE`, `INVALID_RESULT`, and `UNKNOWN_STATUS` are local example errors that prevent endless polling on inconsistent responses or unknown states. Transport errors propagate without retries.
302
+
303
+ <!-- simple-auth-usage:start -->
304
+ ```js
305
+ const accepted = await client.requestDrivingLicense({
306
+ name: 'Hong Gildong',
307
+ birthDate: '19900101',
308
+ phone: '01011112222',
309
+ authProvider: 'kakao'
310
+ });
311
+
312
+ try {
313
+ const result = await pollDataResult(id => client.getDrivingLicense(id), accepted);
314
+ if (result.data.status === 'PARTIAL_SUCCESS') console.warn(result.data.sources);
315
+ console.log(result.data.result);
316
+ } catch (error) {
317
+ if ((error.serviceCode || error.code) === 'RESULT_EXPIRED') {
318
+ console.error('Result expired. Ask the user before starting a new authentication request.');
319
+ } else {
320
+ throw error;
321
+ }
322
+ }
323
+ ```
324
+ <!-- simple-auth-usage:end -->
325
+
326
+ Sources: [APICK development guide](https://apick.app/dev_guide/data_health_checkup) · [MCP 3.5.0 status contract](https://github.com/lead788/apick-mcp/blob/a803abcb81d07377c85f49bb0b670baf0c17ed04/TOOLS.md)
327
+
328
+ `authProvider` is one of the 13 values in `AUTH_PROVIDERS` (kakao, naver, toss, pass, samsung, kb, shinhan, hana, woori, ibk, nh, kakaobank, banksalad). Acceptance is billed at a flat rate; the result is billed only on its first return and free to re-poll afterward. `requestEmployment` takes an optional `insuranceYears` (1-3), `requestPersonalIncome` takes `incomeYears` (1-5), and `requestNpsJoinHistory` takes optional `from`/`to` (`YYYY-MM`). `requestCashReceiptDeduction` takes optional `incomeYears` (1-3) and `requestTaxReturnHistory` takes optional `years` (1-10). The remaining products are `requestDrivingLicense` and `requestHealthCheckup`.
329
+
330
+ ## Skills
331
+
332
+ Search and run reviewed Skills. Each Skill fixes its input format (`input_schema`), output format (`output_schema`) and base amount (`price_points`). A run is charged only when a result in the promised format is returned.
333
+
334
+ For Skills that use generative AI (`usage_priced: true`) the actual usage of each run is added to the base amount, so the charge varies per run. `quoteSkill()` returns the estimated amount (`estimated_points`) and the maximum amount (`max_points`). The maximum is reserved when the run is accepted; when it finishes only the actual amount is charged and the rest is returned. Read the actual charge from `run.billing.charged_points`.
335
+
336
+ ```js
337
+ import { randomUUID } from 'node:crypto';
338
+
339
+ const found = await client.searchSkills({ query: 'product name', limit: 5 });
340
+ const skillId = found.data.items[0].skill_id;
341
+ const detail = await client.getSkill(skillId);
342
+ const input = { product_name: 'Sturdy folding umbrella' };
343
+
344
+ const quote = await client.quoteSkill(skillId, input); // free, valid for 5 minutes
345
+ const idempotencyKey = randomUUID(); // reuse the same value when retrying
346
+ let run = (await client.runSkill(skillId, input, {
347
+ idempotencyKey, quoteId: quote.data.quote_id, maxCostPoints: quote.data.max_points
348
+ })).data;
349
+ while (!['succeeded', 'failed', 'timed_out', 'cancelled'].includes(run.status)) {
350
+ await new Promise(resolve => setTimeout(resolve, 2000));
351
+ run = (await client.getSkillRun(run.run_id)).data;
352
+ }
353
+ console.log(run.status, run.billing, run.result);
354
+ ```
355
+
356
+ | Method | Description |
357
+ | --- | --- |
358
+ | `searchSkills({ query, category, sort, cursor, limit })` | Search. `limit` 1-20; `category` is one of `SKILL_CATEGORIES`, `sort` one of `SKILL_SORTS` |
359
+ | `getSkill(skillId)` | Input and output formats, price, limits, examples |
360
+ | `quoteSkill(skillId, input, { version })` | Validates the input and reports the points to be charged. Does not run |
361
+ | `runSkill(skillId, input, { idempotencyKey, quoteId, maxCostPoints, version, waitSeconds })` | Run. `idempotencyKey` is required; `waitSeconds` 0-20 (default 20) |
362
+ | `getSkillRun(runId)` / `getSkillRunResult(runId)` | Run status (with the result once succeeded) / result only. Results are kept for 7 days |
363
+ | `cancelSkillRun(runId)` | Cancel an unfinished run. Cancelled runs are not charged |
364
+ | `skillUsage({ cursor, limit })` | Your runs. `limit` 1-50 |
365
+
366
+ - `sort` is one of `recommended`, `popular`, `used` (most run), `likes`, `rating`, `new`, `mine` (Skills your account runs most) and `liked` (Skills your account liked). Omit it for registration order.
367
+ - Search and detail responses include `usage_label` (a usage tier such as `1,000회 미만` (under 1,000), `1,000+`, `1만+` (10,000+), not an exact count), `like_count`, `review_count` and `rating_average` (1-5, `null` without reviews). Likes and reviews are left on the Skill page of the APICK website.
368
+ - `sort` is one of `recommended`, `popular`, `used` (most run), `likes`, `rating`, `new`, `mine` (Skills your account runs most) and `liked` (Skills your account liked). Omit it for registration order.
369
+ - Search and detail responses include `usage_label` (a usage tier such as `1,000회 미만` (under 1,000), `1,000+`, `1만+` (10,000+), not an exact count), `like_count`, `review_count` and `rating_average` (1-5, `null` without reviews). Likes and reviews are left on the Skill page of the APICK website.
370
+ - Skills responses are returned as-is in `data`. Read the charge from `data.billing.status` (`reserved`, `captured`, `released`, `partially_refunded`, `refunded`); `meta.cost` is not populated.
371
+ - If a run does not finish within 20 seconds it comes back as `queued` or `running`; poll `getSkillRun`.
372
+ - If a response is lost, call again with the same `idempotencyKey`. Points are charged once. The same key with a different input is rejected with `IDEMPOTENCY_CONFLICT`.
373
+ - Rejections before acceptance throw `ApickApiError` (`serviceCode` such as `INVALID_INPUT`, `INSUFFICIENT_POINTS`, `PAYMENT_REQUIRED`, `PRICE_EXCEEDS_LIMIT`, `RATE_LIMITED`; extra information in `details`). Failures after acceptance are reported through `status` and `failure_code` (`EXECUTION_FAILED`, `OUTPUT_INVALID`, `TIMED_OUT`, `CANCELLED`, `UPSTREAM_UNAVAILABLE`).
374
+ - Skills with `uses_generative_ai: true` produce results with generative AI. Verify them before relying on them.
375
+
376
+ ## Errors and retries
377
+
378
+ `ApickApiError` includes public error information: `code`, optional `serviceCode`, `status`, and `message`. The SDK does not retry automatically because a retry could duplicate an API call and its charge. If your application needs retries, decide explicitly after checking the error code and whether the operation is safe to repeat.
379
+ ## TTS 이전 버전에서 전환 / TTS migration
380
+
381
+ 기존 APICK 목소리와 자동 전환은 종료되었습니다. `createTtsJob`은 Gemini 기본 목소리 `Kore`를 사용합니다. `getTtsQuality`, `retryTtsJob`, `downloadTtsCandidate`의 기존 경로는 HTTP 409를 반환합니다.
382
+
383
+ Legacy APICK voices and automatic fallback have retired. `createTtsJob` now defaults to Gemini voice `Kore`. The legacy quality, retry and candidate endpoints return HTTP 409.
384
+
385
+ ## Video model versions
386
+
387
+ Omitting `version` preserves Seedance 2.5, Veo 3.1 and Kling 3.0. Set `version` and `tier` explicitly to select a generation; jobs are never silently switched to another version. Submission and status responses include `version`.
388
+
389
+ Available generations: Seedance 1.0/1.5/2.0/2.5, including Seedance 2.0 Standard/Fast/Mini; Veo 3.1 (Standard/Fast/Lite); Kling 1.6/2.0/2.1/2.5/2.6/3.0/O1/O3. Veo 3.0 is unavailable. Seedance 2.0 Mini supports 480p/720p and 4–15 seconds. Modes, tiers, resolutions, durations, audio, file limits and prices vary by combination. See the [Seedance](https://apick.app/dev_guide/seedancejobs), [Veo](https://apick.app/dev_guide/veojobs) and [Kling](https://apick.app/dev_guide/klingjobs) version tables. Unsupported combinations are rejected before submission.
390
+
391
+ Seedance reference mode accepts `referenceImages`, `referenceVideos`, and `referenceAudios` (MP3/WAV) when supported by the selected version.
392
+
393
+ ```js
394
+ const job = await client.createVideoJob("kling", "A boat crossing the sea", {
395
+ version: "1.6", tier: "std", mode: "text", duration: 5, audio: false,
396
+ idempotencyKey: "boat-video-0001"
397
+ });
398
+ const status = await client.getVideoJob("kling", job.data.job_id);
399
+ if (status.data.status === "completed") {
400
+ await (await client.downloadVideoResult("kling", job.data.job_id)).save("boat.mp4");
401
+ }
402
+ ```
403
+
404
+ ## Skill artifacts and execution estimates
405
+
406
+ Skill details and quotes provide estimated price and duration ranges and tool call counts. Check the maximum reservation before execution, then use getSkillArtifact(runId, fileId) to download files with authentication. See [Skill artifacts](skill-artifacts.md) for fields and examples.
407
+
408
+ ## Product-specific data guides
409
+
410
+ Product-specific authenticated-data guides describe returned fields, source institutions, use cases, and FAQs. An authentication request starts access to the selected product and returns a transactionId for result polling. Request and response contracts and authentication are unchanged. See the [shared authentication guide](https://apick.app/dev_guide/data) for the flow and error handling.
411
+
412
+
413
+ TTS: [Gemini·ChatGPT·정규화 / engines and normalization](tts.md). `createOpenAiTtsJob`, `listOpenAiTtsVoices`, `getTtsOptions`를 포함한 최신 계약입니다.