apick-api 4.4.0 → 4.8.1

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
@@ -64,6 +64,7 @@ Leave the allowed-IP list blank for unrestricted access. To restrict access, reg
64
64
  | `validatePhone(number)` | 전화번호 유효성 / Phone validation | JSON |
65
65
  | `holidays(year, month)` | 대한민국 공휴일 / Korean holidays | JSON |
66
66
  | `searchAddress(query, options)` | 도로명주소 검색 / Road address search | JSON |
67
+ | `vehicleModelInfo(options)` | 차종별 차량정보 조회 / Vehicle model specifications for cars and motorcycles/scooters (승용차·이륜차 / 표기가 달라도 가장 비슷한 차종 / closest match when spelled differently · 값은 표준 표기 / standard notation values) | JSON |
67
68
  | `ocr(image, options)` | 이미지 OCR / Image OCR | JSON |
68
69
  | `dnsLookup(domain)` | DNS 조회 / DNS lookup | JSON |
69
70
  | `geolocate(address)` | 도메인·IP 위치 / Domain and IP location | JSON |
@@ -90,14 +91,11 @@ Leave the allowed-IP list blank for unrestricted access. To restrict access, reg
90
91
  | `amazonProduct(urlOrAsin)` | 아마존 상품 정보 / Amazon product | JSON |
91
92
  | `xProfile(usernameOrUrl)` | X 공개 프로필·최근 게시물 / X public profile and recent posts | JSON |
92
93
  | `xPost(url)` | X 게시물 / X post | JSON |
93
- | `createInstagramPostsJob(usernameOrUrl, options)` | 인스타그램 게시물 목록 수집 접수(최대 100) / Instagram posts job | JSON |
94
- | `createInstagramCommentsJob(url, options)` | 인스타그램 댓글 수집 접수(최대 15) / Instagram comments job | JSON |
95
- | `createTiktokSearchJob(keyword, options)` | 틱톡 키워드 영상 검색 접수(최대 50) / TikTok keyword search job | JSON |
96
- | `createTiktokVideoJob(url, options)` | 틱톡 영상 정보 접수 / TikTok video job | JSON |
97
- | `createTiktokCommentsJob(url, options)` | 틱톡 댓글 수집 접수(최대 100) / TikTok comments job | JSON |
98
- | `createAmazonReviewsJob(urlOrAsin, options)` | 아마존 리뷰 수집 접수(최대 100) / Amazon reviews job | JSON |
99
- | `createGoogleMapsPlaceJob(placeIdOrUrl, options)` | 구글 지도 장소 상세·별점 분포·대표 리뷰 접수 / Google Maps place details and top reviews job | JSON |
100
- | `getScrapeJob(jobId)` / `waitForScrapeJob(jobId, options)` | 수집 작업 상태·결과(무료, 결과 건수로 정산) / Collection job status and results | JSON |
94
+ | `coupangProduct(urlOrProductId)` | 쿠팡 상품 정보 / Coupang product | JSON |
95
+ | `naverShoppingProduct(url)` | 네이버 브랜드스토어 상품 정보 / Naver brand store product | JSON |
96
+ | `linkedinProfile(url)` | 링크드인 프로필 / LinkedIn profile | JSON |
97
+ | `linkedinCompany(url)` | 링크드인 회사 정보 / LinkedIn company | JSON |
98
+ | `linkedinJob(url)` | 링크드인 채용공고 / LinkedIn job posting | JSON |
101
99
  | `createTtsJob(text, options)` | 한국어 내레이션 작업 접수 / Create TTS job | JSON |
102
100
  | `getTtsJob(jobId)` | TTS 작업 상태 / TTS job status | JSON |
103
101
  | `cancelTtsJob(jobId)` | 대기·생성 중 TTS 작업 취소 / Cancel waiting or processing TTS job | JSON |
@@ -120,6 +118,8 @@ Leave the allowed-IP list blank for unrestricted access. To restrict access, reg
120
118
  | `requestEmployment(input)` / `getEmployment(transactionId)` | 재직·보험료 확인 / Employment & insurance premium check | JSON |
121
119
  | `requestPersonalIncome(input)` / `getPersonalIncome(transactionId)` | 금융소득(이자·배당) 조회 / Financial income (interest/dividend) | JSON |
122
120
  | `requestNpsJoinHistory(input)` / `getNpsJoinHistory(transactionId)` | 국민연금 가입내역조회 / National Pension join history | JSON |
121
+ | `requestNpsDeductionCert(input)` / `getNpsDeductionCert(transactionId)` | 국민연금 소득공제용 납부확인서 / National Pension payment certificate for income deduction | JSON |
122
+ | `requestNpsCareerHistory(input)` / `getNpsCareerHistory(transactionId)` | 국민연금 경력 이력 조회 / National Pension career history | JSON |
123
123
  | `requestDrivingLicense(input)` / `getDrivingLicense(transactionId)` | 운전면허 조회 / Driver's license check | JSON |
124
124
  | `requestHealthCheckup(input)` / `getHealthCheckup(transactionId)` | 국가 건강검진 결과 조회 / National health checkup results | JSON |
125
125
  | `requestCashReceiptDeduction(input)` / `getCashReceiptDeduction(transactionId)` | 현금영수증 소득공제 내역 / Cash receipt income deductions | JSON |
@@ -405,6 +405,8 @@ Acceptance is billed at a flat rate; the result is billed only on its first succ
405
405
  | 재직·보험료 확인 / Employment & insurance premium | `requestEmployment` / `getEmployment` | `insuranceYears` (1–3) |
406
406
  | 금융소득 조회 / Financial income | `requestPersonalIncome` / `getPersonalIncome` | `incomeYears` (1–5) |
407
407
  | 국민연금 가입내역 / NPS join history | `requestNpsJoinHistory` / `getNpsJoinHistory` | `from`, `to` (`YYYY-MM`) |
408
+ | 국민연금 소득공제용 납부확인서 / NPS payment certificate | `requestNpsDeductionCert` / `getNpsDeductionCert` | `years` (1–5) |
409
+ | 국민연금 경력 이력 / NPS career history | `requestNpsCareerHistory` / `getNpsCareerHistory` | `from`, `to` (`YYYY-MM`) |
408
410
  | 운전면허 조회 / Driver's license | `requestDrivingLicense` / `getDrivingLicense` | — |
409
411
  | 국가 건강검진 결과 / Health checkup | `requestHealthCheckup` / `getHealthCheckup` | — |
410
412
  | 현금영수증 소득공제 내역 / Cash receipt deductions | `requestCashReceiptDeduction` / `getCashReceiptDeduction` | `incomeYears` (1–3) |
package/docs/guide.en.md CHANGED
@@ -84,28 +84,14 @@ const post = await client.instagramPost('https://www.instagram.com/p/DeHufcWDCjf
84
84
  const tiktok = await client.tiktokProfile('https://www.tiktok.com/@tiktok');
85
85
  const product = await client.amazonProduct('B0BDHWDR12');
86
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/');
87
92
  ```
88
93
 
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
- ```
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.
109
95
 
110
96
  ## OCR
111
97
 
@@ -318,7 +304,7 @@ try {
318
304
 
319
305
  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)
320
306
 
321
- `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`.
307
+ `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`). `requestNpsDeductionCert` takes optional `years` (1-5) and `requestNpsCareerHistory` 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`.
322
308
 
323
309
  ## Skills
324
310
 
package/docs/guide.ko.md CHANGED
@@ -84,28 +84,14 @@ const post = await client.instagramPost('https://www.instagram.com/p/DeHufcWDCjf
84
84
  const tiktok = await client.tiktokProfile('https://www.tiktok.com/@tiktok');
85
85
  const product = await client.amazonProduct('B0BDHWDR12');
86
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/');
87
92
  ```
88
93
 
89
- ### 수집 작업(목록·댓글·검색·리뷰)
90
-
91
- 여러 건을 모으는 상품은 접수 즉시 `job_id` 를 돌려줍니다. 접수 때 `maxResults` × 단가를 예약하고, 결과를 받을 때 실제 결과 건수만 차감한 뒤 나머지는 돌려드립니다. 실패하거나 대상이 없으면 전액 환불되며 결과는 72시간 보관합니다. 댓글·리뷰 작성자는 공개 사용자명만 제공합니다.
92
-
93
- ```js
94
- const job = await client.createTiktokSearchJob('캠핑 요리', { maxResults: 10, idempotencyKey: 'camping-0001' });
95
- const done = await client.waitForScrapeJob(job.data.job_id); // 기본 10초 간격, 최대 10분
96
- console.log(done.data.status, done.data.charged_point, done.data.items);
97
- ```
98
-
99
- 결과 건수 과금 수집 작업은 결과 1건당 단가로 정산하며, 2026-11-06부터 결과가 1건 이상이면 작업당 기본요금(10P, 인스타그램 댓글 50P)을 최소액으로 받습니다. 결과가 없거나 실패하면 예약한 포인트를 모두 돌려드립니다.
100
-
101
- 구글 지도 장소는 `googleMapsSearch` 결과의 `place_id` 로 상세·별점 분포·대표 리뷰(최대 10건)를 받습니다. 건당 20포인트이고 텍스트는 영어로 제공합니다.
102
-
103
- ```js
104
- const places = await client.googleMapsSearch('강남역 카페');
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
- ```
94
+ 쿠팡은 상품 번호나 상품 주소(`/vp/products/{번호}`)를, 네이버는 브랜드스토어 상품 주소(`brand.naver.com/{스토어}/products/{번호}`)를 받습니다. 링크드인은 프로필(`/in/`)·회사(`/company/`)·채용공고(`/jobs/view/`) 주소를 받습니다.
109
95
 
110
96
  ## OCR
111
97
 
@@ -321,7 +307,7 @@ try {
321
307
 
322
308
  근거: [APICK 개발가이드](https://apick.app/dev_guide/data_health_checkup) · [MCP 3.5.0 상태 계약](https://github.com/lead788/apick-mcp/blob/a803abcb81d07377c85f49bb0b670baf0c17ed04/TOOLS.md)
323
309
 
324
- `authProvider`는 `AUTH_PROVIDERS`(13종: kakao, naver, toss, pass, samsung, kb, shinhan, hana, woori, ibk, nh, kakaobank, banksalad) 중 하나입니다. 접수는 정액 과금, 결과는 최초 반환에서만 과금되며 재조회는 무료입니다. `requestEmployment`는 `insuranceYears`(1~3), `requestPersonalIncome`은 `incomeYears`(1~5), `requestNpsJoinHistory`는 `from`/`to`(`YYYY-MM`) 선택 입력을 받습니다. `requestCashReceiptDeduction`은 `incomeYears`(1~3), `requestTaxReturnHistory`는 `years`(1~10) 선택 입력을 받습니다. 나머지 상품은 `requestDrivingLicense`, `requestHealthCheckup`입니다.
310
+ `authProvider`는 `AUTH_PROVIDERS`(13종: kakao, naver, toss, pass, samsung, kb, shinhan, hana, woori, ibk, nh, kakaobank, banksalad) 중 하나입니다. 접수는 정액 과금, 결과는 최초 반환에서만 과금되며 재조회는 무료입니다. `requestEmployment`는 `insuranceYears`(1~3), `requestPersonalIncome`은 `incomeYears`(1~5), `requestNpsJoinHistory`는 `from`/`to`(`YYYY-MM`) 선택 입력을 받습니다. `requestNpsDeductionCert`는 `years`(1~5), `requestNpsCareerHistory`는 `from`/`to`(`YYYY-MM`) 선택 입력을 받습니다. `requestCashReceiptDeduction`은 `incomeYears`(1~3), `requestTaxReturnHistory`는 `years`(1~10) 선택 입력을 받습니다. 나머지 상품은 `requestDrivingLicense`, `requestHealthCheckup`입니다.
325
311
 
326
312
  ## Skills
327
313
 
package/package.json CHANGED
@@ -1,71 +1,71 @@
1
- {
2
- "name": "apick-api",
3
- "version": "4.4.0",
4
- "description": "Official zero-dependency Node.js client for APICK data, AI, and image APIs. 에이픽 데이터·AI·이미지 API 공식 Node.js SDK.",
5
- "type": "module",
6
- "main": "./src/index.cjs",
7
- "module": "./src/index.js",
8
- "types": "./src/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./src/index.d.ts",
12
- "import": "./src/index.js",
13
- "require": "./src/index.cjs"
14
- }
15
- },
16
- "files": [
17
- "src",
18
- "docs",
19
- "examples",
20
- "README.md",
21
- "CHANGELOG.md",
22
- "SECURITY.md",
23
- "LICENSE"
24
- ],
25
- "sideEffects": false,
26
- "engines": {
27
- "node": ">=18"
28
- },
29
- "scripts": {
30
- "test": "node --test && npm run test:types",
31
- "test:types": "tsc -p tsconfig.json",
32
- "test:coverage": "node --test --experimental-test-coverage",
33
- "prepublishOnly": "npm test"
34
- },
35
- "devDependencies": {
36
- "typescript": "^7.0.2"
37
- },
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/lead788/apick-api.git"
41
- },
42
- "bugs": {
43
- "url": "https://github.com/lead788/apick-api/issues"
44
- },
45
- "homepage": "https://apick.app/dev_guide",
46
- "license": "MIT",
47
- "author": "APICK",
48
- "publishConfig": {
49
- "access": "public",
50
- "provenance": false
51
- },
52
- "keywords": [
53
- "apick",
54
- "api",
55
- "sdk",
56
- "korea",
57
- "korean-api",
58
- "business-registry",
59
- "parcel-tracking",
60
- "address",
61
- "ocr",
62
- "whois",
63
- "dns",
64
- "google-search",
65
- "pdf",
66
- "excel",
67
- "tts-jobs",
68
- "text-summary",
69
- "image-generation"
70
- ]
71
- }
1
+ {
2
+ "name": "apick-api",
3
+ "version": "4.8.1",
4
+ "description": "Official zero-dependency Node.js client for APICK data, AI, and image APIs. 에이픽 데이터·AI·이미지 API 공식 Node.js SDK.",
5
+ "type": "module",
6
+ "main": "./src/index.cjs",
7
+ "module": "./src/index.js",
8
+ "types": "./src/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.d.ts",
12
+ "import": "./src/index.js",
13
+ "require": "./src/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "src",
18
+ "docs",
19
+ "examples",
20
+ "README.md",
21
+ "CHANGELOG.md",
22
+ "SECURITY.md",
23
+ "LICENSE"
24
+ ],
25
+ "sideEffects": false,
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "scripts": {
30
+ "test": "node --test && npm run test:types",
31
+ "test:types": "tsc -p tsconfig.json",
32
+ "test:coverage": "node --test --experimental-test-coverage",
33
+ "prepublishOnly": "npm test"
34
+ },
35
+ "devDependencies": {
36
+ "typescript": "^7.0.2"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/lead788/apick-api.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/lead788/apick-api/issues"
44
+ },
45
+ "homepage": "https://apick.app/dev_guide",
46
+ "license": "MIT",
47
+ "author": "APICK",
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "provenance": false
51
+ },
52
+ "keywords": [
53
+ "apick",
54
+ "api",
55
+ "sdk",
56
+ "korea",
57
+ "korean-api",
58
+ "business-registry",
59
+ "parcel-tracking",
60
+ "address",
61
+ "ocr",
62
+ "whois",
63
+ "dns",
64
+ "google-search",
65
+ "pdf",
66
+ "excel",
67
+ "tts-jobs",
68
+ "text-summary",
69
+ "image-generation"
70
+ ]
71
+ }
package/src/index.cjs CHANGED
@@ -26,6 +26,7 @@ const SERVICE_DEFINITIONS = Object.freeze({
26
26
  validatePhone: { endpoint: '/rest/check_phone_valid', timeoutMs: 20_000, output: 'json' },
27
27
  holidays: { endpoint: '/rest/holiday_info', timeoutMs: 35_000, output: 'json' },
28
28
  searchAddress: { endpoint: '/rest/search_juso', timeoutMs: 20_000, output: 'json' },
29
+ vehicleModelInfo: { endpoint: '/rest/vehicle_model_info', timeoutMs: 20_000, output: 'json' },
29
30
  ocr: { endpoint: '/rest/ocr', timeoutMs: 35_000, output: 'json' },
30
31
  maskResidentNumber: { endpoint: '/rest/hide_rrn', timeoutMs: 35_000, output: 'binary', filename: 'masked.png' },
31
32
  maskResidenceCard: { endpoint: '/rest/identity_document_residence_card', timeoutMs: 35_000, output: 'json' },
@@ -47,7 +48,11 @@ const SERVICE_DEFINITIONS = Object.freeze({
47
48
  amazonProduct: { endpoint: '/rest/amazon_product', timeoutMs: 110_000, output: 'json' },
48
49
  xProfile: { endpoint: '/rest/x_profile', timeoutMs: 110_000, output: 'json' },
49
50
  xPost: { endpoint: '/rest/x_post', timeoutMs: 110_000, output: 'json' },
50
- getScrapeJob: { endpoint: '/rest/scrape_jobs', timeoutMs: 35_000, output: 'json' },
51
+ coupangProduct: { endpoint: '/rest/coupang_product', timeoutMs: 110_000, output: 'json' },
52
+ naverShoppingProduct: { endpoint: '/rest/naver_shopping_product', timeoutMs: 110_000, output: 'json' },
53
+ linkedinProfile: { endpoint: '/rest/linkedin_profile', timeoutMs: 110_000, output: 'json' },
54
+ linkedinCompany: { endpoint: '/rest/linkedin_company', timeoutMs: 110_000, output: 'json' },
55
+ linkedinJob: { endpoint: '/rest/linkedin_job', timeoutMs: 110_000, output: 'json' },
51
56
  screenshot: { endpoint: '/rest/url_screenshot', timeoutMs: 75_000, output: 'binary', filename: 'screenshot.jpeg' },
52
57
  youtubeMetadata: { endpoint: '/rest/youtube_metadata', timeoutMs: 60_000, output: 'json' },
53
58
  youtubeThumbnail: { endpoint: '/rest/youtube_thumbnail', timeoutMs: 60_000, output: 'binary', filename: 'thumbnail.jpg' },
@@ -68,6 +73,8 @@ const SERVICE_DEFINITIONS = Object.freeze({
68
73
  requestEmployment: { endpoint: '/rest/req_employment', timeoutMs: 35_000, output: 'json' },
69
74
  requestPersonalIncome: { endpoint: '/rest/req_personal_income', timeoutMs: 35_000, output: 'json' },
70
75
  requestNpsJoinHistory: { endpoint: '/rest/req_nps_join_history', timeoutMs: 35_000, output: 'json' },
76
+ requestNpsDeductionCert: { endpoint: '/rest/req_nps_deduction_cert', timeoutMs: 35_000, output: 'json' },
77
+ requestNpsCareerHistory: { endpoint: '/rest/req_nps_career_history', timeoutMs: 35_000, output: 'json' },
71
78
  requestDrivingLicense: { endpoint: '/rest/req_driving_license', timeoutMs: 35_000, output: 'json' },
72
79
  requestHealthCheckup: { endpoint: '/rest/req_health_checkup', timeoutMs: 35_000, output: 'json' },
73
80
  requestCashReceiptDeduction: { endpoint: '/rest/req_cash_receipt_deduction', timeoutMs: 35_000, output: 'json' },
@@ -94,33 +101,26 @@ function profileInput(value) {
94
101
  return /^https?:\/\//i.test(text) ? { url: text } : { username: text.replace(/^@/, '') };
95
102
  }
96
103
 
97
- // 구글 지도 장소 입력: 주소면 url, 아니면 place_id(googleMapsSearch 결과의 place_id)로 보낸다.
98
- function placeInput(value) {
99
- const text = requiredString('placeIdOrUrl', value, 2048);
100
- return /^https?:\/\//i.test(text) ? { url: text } : { place_id: text };
101
- }
102
-
103
104
  // 아마존 상품 입력: 주소면 url, 10자리 ASIN 이면 asin 으로 보낸다.
104
105
  function amazonInput(value) {
105
106
  const text = requiredString('urlOrAsin', value, 2048);
106
107
  return /^https?:\/\//i.test(text) ? { url: text } : { asin: text.toUpperCase() };
107
108
  }
108
109
 
109
- const SCRAPE_JOB_PRODUCTS = Object.freeze({ instagram_posts: 100, instagram_comments: 15, tiktok_search: 50, tiktok_video: 1, tiktok_comments: 100, amazon_reviews: 100, google_maps_place: 1 });
110
+ // 쿠팡 상품 입력: 주소면 url, 상품 번호면 product_id 로 보낸다.
111
+ function coupangInput(value) {
112
+ const text = requiredString('urlOrProductId', value, 2048);
113
+ return /^https?:\/\//i.test(text) ? { url: text } : { product_id: text };
114
+ }
110
115
 
111
- // 수집 작업 공통 옵션: maxResults(상품별 상한), idempotencyKey(응답을 못 받아 다시 보낼 때 같은 접수로 처리).
112
- function scrapeJobOptions(product, payload, options) {
113
- const config = options || {};
114
- if (config.maxResults !== undefined && SCRAPE_JOB_PRODUCTS[product] > 1) {
115
- const max = Number(config.maxResults);
116
- if (!Number.isInteger(max) || max < 1 || max > SCRAPE_JOB_PRODUCTS[product]) throw new RangeError(`maxResults must be an integer from 1 to ${SCRAPE_JOB_PRODUCTS[product]}.`);
117
- payload.max_results = max;
118
- }
119
- if (config.idempotencyKey !== undefined) {
120
- if (typeof config.idempotencyKey !== 'string' || !/^[A-Za-z0-9_.:-]{8,128}$/.test(config.idempotencyKey)) throw new TypeError('idempotencyKey must be 8-128 characters of letters, digits, and . _ : -');
121
- payload.idempotency_key = config.idempotencyKey;
122
- }
123
- return payload;
116
+ // 네이버 브랜드스토어 상품 입력: 상품 주소만 받는다.
117
+ function naverInput(value) {
118
+ return { url: requiredString('url', value, 2048) };
119
+ }
120
+
121
+ // LinkedIn 입력: 프로필(/in/), 회사(/company/), 채용공고(/jobs/view/) 주소.
122
+ function linkedinInput(value) {
123
+ return { url: requiredString('url', value, 2048) };
124
124
  }
125
125
 
126
126
  function requiredString(name, value, maxLength) {
@@ -264,6 +264,10 @@ function optionalSkillText(name, value, maxLength) {
264
264
  return requiredString(name, String(value), maxLength);
265
265
  }
266
266
 
267
+ function optionalText(name, value, maxLength) {
268
+ return optionalSkillText(name, value, maxLength);
269
+ }
270
+
267
271
  function numberOrNull(value) {
268
272
  if (value === undefined || value === null || value === '') return null;
269
273
  const number = Number(value);
@@ -563,6 +567,19 @@ class ApickClient {
563
567
  });
564
568
  }
565
569
 
570
+ // 브랜드·차명·연식·등급으로 제원·사양·등급 후보를 조회한다.
571
+ // 차명(model)을 비우면 브랜드 목록 또는 그 브랜드의 차종 목록을 무료로 돌려준다.
572
+ // 제원 값은 표준 표기로만 내려간다(연료·구동방식·변속기·차체형상·에너지효율등급, 여러 값은 "+" 연결).
573
+ vehicleModelInfo(options) {
574
+ const config = options || {};
575
+ return this._call('vehicleModelInfo', {
576
+ maker: optionalText('maker', config.maker, 60),
577
+ model: optionalText('model', config.model, 60),
578
+ modelYear: optionalText('modelYear', config.modelYear, 60),
579
+ trim: optionalText('trim', config.trim, 60)
580
+ });
581
+ }
582
+
566
583
  async ocr(image, options) {
567
584
  const upload = await normalizeImage(image, options);
568
585
  const form = new FormData();
@@ -638,7 +655,6 @@ class ApickClient {
638
655
  googleMapsSearch(keyword) {
639
656
  return this._call('googleMapsSearch', { keyword: requiredString('keyword', keyword, 200) });
640
657
  }
641
-
642
658
  // keyword 로 구글을 검색했을 때 domain 이 1~100위 중 몇 위인지 확인한다.
643
659
  googleRankCheck(keyword, domain) {
644
660
  return this._call('googleRankCheck', {
@@ -674,58 +690,29 @@ class ApickClient {
674
690
  return this._call('xPost', { url: requiredString('url', url, 2048) });
675
691
  }
676
692
 
677
- // 수집 작업: 접수 즉시 job_id 를 돌려준다. maxResults × 단가를 예약하고 결과를 받을 때 실제 건수만 차감한다.
678
- _createScrapeJob(product, payload, options) {
679
- return this._call('getScrapeJob', scrapeJobOptions(product, payload, options), null, { endpoint: '/rest/' + product + '/jobs', json: true });
680
- }
681
-
682
- createInstagramPostsJob(usernameOrUrl, options) {
683
- return this._createScrapeJob('instagram_posts', profileInput(usernameOrUrl), options);
693
+ // 상품 주소(/vp/products/{번호}) 또는 상품 번호. 처리에 보통 25~40초가 걸린다.
694
+ coupangProduct(urlOrProductId) {
695
+ return this._call('coupangProduct', coupangInput(urlOrProductId));
684
696
  }
685
697
 
686
- createInstagramCommentsJob(url, options) {
687
- return this._createScrapeJob('instagram_comments', { url: requiredString('url', url, 2048) }, options);
698
+ // 네이버 브랜드스토어 상품 주소(/{스토어}/products/{번호}). 스마트스토어 직링크는 받지 않는다.
699
+ naverShoppingProduct(url) {
700
+ return this._call('naverShoppingProduct', naverInput(url));
688
701
  }
689
702
 
690
- createTiktokSearchJob(keyword, options) {
691
- return this._createScrapeJob('tiktok_search', { keyword: requiredString('keyword', keyword, 100) }, options);
703
+ // LinkedIn 프로필 주소(/in/{아이디}). 처리에 보통 20초 안팎이 걸린다.
704
+ linkedinProfile(url) {
705
+ return this._call('linkedinProfile', linkedinInput(url));
692
706
  }
693
707
 
694
- createTiktokVideoJob(url, options) {
695
- return this._createScrapeJob('tiktok_video', { url: requiredString('url', url, 2048) }, options);
708
+ // LinkedIn 회사 주소(/company/{아이디}).
709
+ linkedinCompany(url) {
710
+ return this._call('linkedinCompany', linkedinInput(url));
696
711
  }
697
712
 
698
- createTiktokCommentsJob(url, options) {
699
- return this._createScrapeJob('tiktok_comments', { url: requiredString('url', url, 2048) }, options);
700
- }
701
-
702
- createAmazonReviewsJob(urlOrAsin, options) {
703
- return this._createScrapeJob('amazon_reviews', amazonInput(urlOrAsin), options);
704
- }
705
-
706
- // 구글 지도 장소 상세·대표 리뷰(건당 고정가). 텍스트는 영어로 온다.
707
- createGoogleMapsPlaceJob(placeIdOrUrl, options) {
708
- return this._createScrapeJob('google_maps_place', placeInput(placeIdOrUrl), options);
709
- }
710
-
711
- // 상태·결과 조회(무료). 끝난 작업은 실제 결과 건수로 정산돼 있다.
712
- getScrapeJob(jobId) {
713
- if (typeof jobId !== 'string' || !/^[a-f0-9]{32}$/.test(jobId)) throw new TypeError('jobId must be a 32-character job ID.');
714
- return this._call('getScrapeJob', null, null, { endpoint: '/rest/scrape_jobs/' + jobId, method: 'GET', timeoutMs: 70_000 });
715
- }
716
-
717
- // completed 또는 failed 가 될 때까지 조회한다. 기본 10초 간격, 최대 10분.
718
- async waitForScrapeJob(jobId, options) {
719
- const config = options || {};
720
- const intervalMs = Math.max(3_000, positiveInteger('intervalMs', config.intervalMs, 10_000));
721
- const deadline = Date.now() + positiveInteger('timeoutMs', config.timeoutMs, 600_000);
722
- for (;;) {
723
- const result = await this.getScrapeJob(jobId);
724
- const status = result && result.data && result.data.status;
725
- if (status === 'completed' || status === 'failed') return result;
726
- if (Date.now() + intervalMs > deadline) throw new ApickApiError('Scrape job did not finish in time.', { code: 'APICK_TIMEOUT' });
727
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
728
- }
713
+ // LinkedIn 채용공고 주소(/jobs/view/{번호}).
714
+ linkedinJob(url) {
715
+ return this._call('linkedinJob', linkedinInput(url));
729
716
  }
730
717
 
731
718
  screenshot(url) {
@@ -1075,6 +1062,31 @@ class ApickClient {
1075
1062
  return this._call('requestNpsJoinHistory', { transactionId: normalizeTransactionId(transactionId) }, null, { endpoint: '/rest/get_nps_join_history' });
1076
1063
  }
1077
1064
 
1065
+ requestNpsDeductionCert(input) {
1066
+ const payload = dataRequestInput(input);
1067
+ const years = optionalRangeInteger('years', (input || {}).years, 1, 5);
1068
+ if (years !== undefined) payload.years = years;
1069
+ return this._call('requestNpsDeductionCert', payload);
1070
+ }
1071
+
1072
+ getNpsDeductionCert(transactionId) {
1073
+ return this._call('requestNpsDeductionCert', { transactionId: normalizeTransactionId(transactionId) }, null, { endpoint: '/rest/get_nps_deduction_cert' });
1074
+ }
1075
+
1076
+ requestNpsCareerHistory(input) {
1077
+ const payload = dataRequestInput(input);
1078
+ const config = input || {};
1079
+ const from = normalizeYearMonth('from', config.from);
1080
+ const to = normalizeYearMonth('to', config.to);
1081
+ if (from !== undefined) payload.from = from;
1082
+ if (to !== undefined) payload.to = to;
1083
+ return this._call('requestNpsCareerHistory', payload);
1084
+ }
1085
+
1086
+ getNpsCareerHistory(transactionId) {
1087
+ return this._call('requestNpsCareerHistory', { transactionId: normalizeTransactionId(transactionId) }, null, { endpoint: '/rest/get_nps_career_history' });
1088
+ }
1089
+
1078
1090
  requestDrivingLicense(input) {
1079
1091
  return this._call('requestDrivingLicense', dataRequestInput(input));
1080
1092
  }