# ScrapeLinkedIn API Send us a LinkedIn URL. We send you back the profile data. **$0.01 per lookup. You only pay when we find the data.** Base URL: `https://api.scrapelinkedin.com` ## How It Works You give us a LinkedIn profile URL. Our agents visit that profile, read everything on it, and return the data to you as JSON — name, experience, education, all of it. Two ways to use it: - **Single lookup** — send one URL, get one profile back - **Batch lookup** — send up to 1,000 URLs at once, get all results back Results are cached for 24 hours. If you look up the same profile twice in one day, the second lookup is instant and free. ## Quick Start ### 1. Create an account ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{"email": "you@company.com"}' ``` You'll get a 6-digit verification code in your email. ### 2. Verify your email ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/auth/verify \ -H "Content-Type: application/json" \ -d '{"email": "you@company.com", "code": "123456"}' ``` ### 3. Get your API key ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/auth/api-key \ -H "Content-Type: application/json" \ -d '{"email": "you@company.com"}' ``` Response: ```json { "api_key": "li_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ...", "key_prefix": "li_live_aBcD", "credits_balance": 5, "message": "Save this key -- it will not be shown again." } ``` **Save your API key immediately.** You will never see it again. We only store a hash of it. Your first key comes with **5 free credits** (5 free lookups). ### 4. Look up a profile ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/scrape \ -H "Content-Type: application/json" \ -H "X-API-Key: li_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ..." \ -d '{"linkedin_url": "https://linkedin.com/in/johndoe"}' ``` Response: ```json { "data": { "id": "507f1f77bcf86cd799439011", "linkedin_url": "https://linkedin.com/in/johndoe", "status": "pending", "profile_data": null, "created_at": "2026-03-05T10:30:00Z", "updated_at": "2026-03-05T10:30:00Z" }, "credits_charged": 1, "credits_remaining": 4 } ``` The lookup happens asynchronously. The status starts as `"pending"`. Poll the result by ID: ```bash curl https://api.scrapelinkedin.com/api/v1/scrape/507f1f77bcf86cd799439011 \ -H "X-API-Key: li_live_..." ``` When it's done, `status` changes to `"completed"` and `profile_data` contains the scraped data: ```json { "id": "507f1f77bcf86cd799439011", "linkedin_url": "https://linkedin.com/in/johndoe", "status": "completed", "profile_data": { "full_name": "John Doe", "experience": "Senior Engineer at Google (2020-present), Software Engineer at Meta (2017-2020)", "education": "MIT, BS Computer Science (2017)" }, "created_at": "2026-03-05T10:30:00Z", "updated_at": "2026-03-05T10:30:15Z" } ``` ## Batch Lookups Send up to 1,000 LinkedIn URLs in one request: ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/scrape/batch \ -H "Content-Type: application/json" \ -H "X-API-Key: li_live_..." \ -d '{ "linkedin_urls": [ "https://linkedin.com/in/person1", "https://linkedin.com/in/person2", "https://linkedin.com/in/person3" ] }' ``` Response: ```json { "data": { "batch_id": "507f1f77bcf86cd799439022", "submitted": 3, "cached": 0, "total": 3, "status": "pending", "jobs": [...] }, "credits_charged": 3, "credits_remaining": 97 } ``` Check batch progress: ```bash curl https://api.scrapelinkedin.com/api/v1/scrape/batch/507f1f77bcf86cd799439022 \ -H "X-API-Key: li_live_..." ``` Response: ```json { "id": "507f1f77bcf86cd799439022", "status": "completed", "total": 3, "completed": 2, "failed": 1, "profiles": [ {"linkedin_url": "https://linkedin.com/in/person1", "status": "completed", "profile_data": {"full_name": "..."}}, {"linkedin_url": "https://linkedin.com/in/person2", "status": "completed", "profile_data": {"full_name": "..."}}, {"linkedin_url": "https://linkedin.com/in/person3", "status": "failed", "error": "Profile not found"} ] } ``` ### Batch callbacks If you pass a `callback_url`, we'll POST the results to it when the batch finishes: ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/scrape/batch \ -H "Content-Type: application/json" \ -H "X-API-Key: li_live_..." \ -d '{ "linkedin_urls": ["https://linkedin.com/in/person1"], "callback_url": "https://your-server.com/webhook" }' ``` When the batch completes, we POST to your URL: ```json { "batch_id": "507f1f77bcf86cd799439022", "status": "completed", "total": 1, "completed": 1, "failed": 0, "profiles": [...] } ``` ## Pricing **$0.01 per successful lookup. No tiers. No bulk discounts.** - If we can't find the profile, you don't pay — the credit is automatically refunded. - If you look up a URL that already failed before, we return the cached failure for free. - Cached results (within 24 hours) are free. - GET requests (checking status, viewing results) are always free. ### Credit packs | Pack | Credits | Price | |------|---------|-------| | 100 | 100 lookups | $1.00 | | 1,000 | 1,000 lookups | $10.00 | | 10,000 | 10,000 lookups | $100.00 | Buy credits: ```bash curl -X POST https://api.scrapelinkedin.com/api/v1/credits/purchase \ -H "Content-Type: application/json" \ -H "X-API-Key: li_live_..." \ -d '{"pack_id": "1000"}' ``` This returns a Stripe Checkout URL. Open it in your browser to pay. Credits are added to your account automatically after payment. ## Authentication Every request (except registration, verification, and health check) requires your API key: ``` X-API-Key: li_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ... ``` Pass it as an HTTP header. **If you lose your key**, call the `/auth/api-key` endpoint again with your email. It generates a new key and transfers your remaining credits to it. The old key stops working. ## Rate Limits - **10 POST requests per minute** per API key - **5,000 lookups per day** per API key - GET requests are not rate-limited If you hit a rate limit, you get a `429` response with a `retry_after_seconds` field. ## Error Codes | Code | Meaning | |------|---------| | 200 | Success | | 201 | Created (new scrape submitted) | | 400 | Bad request (invalid URL, missing fields) | | 401 | Missing or invalid API key | | 402 | Not enough credits — buy more | | 404 | Profile or batch not found | | 413 | Request body too large (max 1 MB) | | 422 | Invalid LinkedIn URL format | | 429 | Rate limit exceeded — wait and retry | ## API Reference ### Scraping | Method | Endpoint | Auth | Cost | Description | |--------|----------|------|------|-------------| | POST | `/api/v1/scrape` | Yes | 1 credit | Look up a single LinkedIn profile | | POST | `/api/v1/scrape/batch` | Yes | N credits | Look up up to 1,000 profiles | | GET | `/api/v1/scrape/{id}` | Yes | Free | Check result by ID | | GET | `/api/v1/scrape?linkedin_url=...` | Yes | Free | Check result by LinkedIn URL | | GET | `/api/v1/scrape/batch/{batch_id}` | Yes | Free | Check batch progress | ### Account | Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | POST | `/api/v1/auth/register` | No | Create account (sends verification code) | | POST | `/api/v1/auth/verify` | No | Verify email with 6-digit code | | POST | `/api/v1/auth/api-key` | No | Generate API key (5 free credits) | | GET | `/api/v1/account` | Yes | View balance, usage, recent queries | | DELETE | `/api/v1/account` | Yes | Delete your account | ### Credits | Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | GET | `/api/v1/credits/packs` | No | List available credit packs | | POST | `/api/v1/credits/purchase` | Yes | Get Stripe Checkout URL to buy credits | ### System | Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | GET | `/health` | No | Health check | ## Endpoint Details ### POST /api/v1/scrape Submit a single LinkedIn profile URL for scraping. **Request:** ```json { "linkedin_url": "https://linkedin.com/in/johndoe", "force": false } ``` - `linkedin_url` (required): Must contain `linkedin.com/in/` or `linkedin.com/company/` - `force` (optional, default false): Set to `true` to bypass cached failures and retry **Response (201):** ```json { "data": { "id": "507f1f77bcf86cd799439011", "linkedin_url": "https://linkedin.com/in/johndoe", "status": "pending", "profile_data": null, "error": null, "batch_id": null, "created_at": "2026-03-05T10:30:00Z", "updated_at": "2026-03-05T10:30:00Z" }, "credits_charged": 1, "credits_remaining": 4 } ``` **Possible status values:** `pending`, `completed`, `failed`, `timed_out` **Profile data structure (when completed):** ```json { "full_name": "John Doe", "experience": "Senior Engineer at Google (2020-present), Software Engineer at Meta (2017-2020)", "education": "MIT, BS Computer Science (2017)" } ``` ### POST /api/v1/scrape/batch Submit up to 1,000 LinkedIn URLs for batch scraping. **Request:** ```json { "linkedin_urls": [ "https://linkedin.com/in/person1", "https://linkedin.com/in/person2" ], "callback_url": "https://your-server.com/webhook" } ``` - `linkedin_urls` (required): Array of LinkedIn URLs, max 1,000 - `callback_url` (optional): URL to POST results to when batch completes **Response (201):** ```json { "data": { "batch_id": "507f1f77bcf86cd799439022", "submitted": 2, "cached": 0, "total": 2, "status": "pending", "jobs": [...] }, "credits_charged": 2, "credits_remaining": 98 } ``` ### GET /api/v1/scrape/{id} Check result of a single scrape by its ID. Free, no credit cost. ### GET /api/v1/scrape?linkedin_url=... Check result by LinkedIn URL. Free, no credit cost. ### GET /api/v1/scrape/batch/{batch_id} Check batch status and get all profile results. **Response:** ```json { "id": "507f1f77bcf86cd799439022", "status": "completed", "total": 3, "completed": 2, "failed": 1, "callback_url": null, "callback_status": null, "created_at": "2026-03-05T10:30:00Z", "updated_at": "2026-03-05T10:35:00Z", "profiles": [ { "id": "...", "linkedin_url": "https://linkedin.com/in/person1", "status": "completed", "profile_data": {"full_name": "...", "experience": "...", "education": "..."}, "error": null } ] } ``` ### POST /api/v1/auth/register Create a new account. Sends a 6-digit verification code to your email. **Request:** ```json {"email": "you@company.com"} ``` **Response:** ```json {"message": "Verification code sent", "email": "you@company.com"} ``` ### POST /api/v1/auth/verify Verify your email with the 6-digit code. **Request:** ```json {"email": "you@company.com", "code": "123456"} ``` ### POST /api/v1/auth/api-key Generate an API key. First key comes with 5 free credits. Calling again generates a new key (credits transfer, old key stops working). **Request:** ```json {"email": "you@company.com"} ``` **Response:** ```json { "api_key": "li_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ...", "key_prefix": "li_live_aBcD", "credits_balance": 5, "message": "Save this key -- it will not be shown again." } ``` ### GET /api/v1/account View your account details, balance, and recent usage. **Response:** ```json { "email": "you@company.com", "credits_balance": 95, "total_queries": 10, "total_charged": 8, "key_prefix": "li_live_aBcD", "is_active": true, "recent_usage": [ { "linkedin_url": "https://linkedin.com/in/johndoe", "credits_charged": 1, "refunded": false, "created_at": "2026-03-05T10:30:00Z" } ] } ``` ### GET /api/v1/credits/packs List available credit packs and prices. **Response:** ```json { "packs": { "100": {"credits": 100, "price_cents": 100, "label": "100 credits - $1.00"}, "1000": {"credits": 1000, "price_cents": 1000, "label": "1,000 credits - $10.00"}, "10000": {"credits": 10000, "price_cents": 10000, "label": "10,000 credits - $100.00"} } } ``` ### POST /api/v1/credits/purchase Start a Stripe Checkout session to buy credits. **Request:** ```json {"pack_id": "1000"} ``` **Response:** ```json { "checkout_url": "https://checkout.stripe.com/...", "pack": "1000", "credits": 1000, "price_cents": 1000 } ``` ### GET /health Health check. Returns service status and database connectivity. **Response:** ```json { "status": "ok", "db": "connected", "service": "linkedin-scraper" } ``` ## Credit System Credits are deducted when you submit a scrape request. If the scrape fails (profile not found, invalid URL, etc.), the credit is automatically refunded. You only pay for data we actually return. For batch requests, all credits are deducted upfront. One credit is refunded per failed lookup as results come back. **Refund flow:** 1. Submit a URL → 1 credit deducted 2. Agents try to scrape the profile 3. If it fails → credit automatically refunded to your balance 4. Usage record marked as `refunded: true` ## Caching - **Successful lookups** cached for 24 hours. Repeat lookups are instant and free. - **Failed lookups** also cached. Pass `"force": true` to bypass and retry. ## Security - API keys hashed with SHA-256 before storage (plaintext never stored) - Timing-safe comparison prevents side-channel attacks - Each key has its own credit balance, rate limits, and usage history - Keys can be regenerated (credits transfer, old key revoked) - Callback URLs validated to block internal IP addresses (SSRF protection)