# FinalScout API Reference > FinalScout is an email-finding API. Given a LinkedIn profile URL, article URL, or a person's full name and company domain, it finds and verifies the professional email address. It also enriches person and company data from LinkedIn. Base URL: https://api.finalscout.com Waterfall Base URL: https://api-waterfall.finalscout.com ## Authentication All endpoints require an API key passed as a request header: ``` Authorization: ``` Obtain your API key at: https://finalscout.com/app/api/settings ## Credits Every successful email find costs 1 credit. Every response includes: - `credits_consumed` (integer): credits used for the request - `credits_available` (integer): remaining credits in your account ## Revision History - 1.5.0 (2026-05-16): Added `enable_work_email` param to linkedin find endpoints - 1.4.0 (2026-02-03): Unified credit system; added `email_is_catchall` and `email_score` fields - 1.3.0 (2026-01-17): Added `/v1/find/bulk/dump`; added webhook schemas - 1.2.0 (2025-12-19): Added Waterfall endpoints at https://api-waterfall.finalscout.com - 1.1.0 (2025-06-19): Added `meta_data` parameter to single and bulk find APIs - 1.0.0 (2024-01-19): Initial release --- ## Endpoints ### Single Find #### POST /v1/find/linkedin/single Find an email address from a LinkedIn profile URL. Request body (JSON): - `person` (object, required) - `linkedin_url` (string, required): LinkedIn profile URL. Supported formats: - https://www.linkedin.com/in/williamhgates/ - https://www.linkedin.com/sales/people/ACoAAA8BYqEB...,name,sSHS - https://www.linkedin.com/in/ACoAAA8BYqEB... - `full_name` (string, optional) - `avatar` (string, optional) - `current_positions` (array, optional): array of objects with `company_name`, `company_linkedin_url`, `title`, `location` - `meta_data` (object, optional): custom key-value pairs returned in response and webhook - `tags` (array of strings, optional): organize contacts in the web app - `enable_work_email` (boolean, optional, default true): whether to find work emails - `enable_personal_email` (boolean, optional, default false): whether to find personal emails when work email is not found - `enable_generic_email` (boolean, optional, default false): whether to find generic emails (e.g. info@company.com) when work email is not found - `webhook_url` (string, optional): URL to receive `single_task.finished` event when done Response 200: ```json { "id": "2mcxxP", "status": "Running | Complete", "contact": { ...Contact object... }, "tags": ["tag1"], "meta_data": {}, "credits_consumed": 1, "credits_available": 19826.5 } ``` Response 403: `{ "credits_required": 1, "credits_available": 0 }` --- #### POST /v1/find/author/single Find an email address for the author of a news article. Request body (JSON): - `person` (object, required) - `article_url` (string, required): URL of the news article - `meta_data` (object, optional) - `tags` (array of strings, optional) - `webhook_url` (string, optional) Response 200: same as `/v1/find/linkedin/single` --- #### POST /v1/find/professional/single Find an email address by full name and company domain. Request body (JSON): - `person` (object, required) - `full_name` (string, required) - `domain` (string, required): company domain, e.g. microsoft.com - `domain_extra` (array of strings, optional): additional domains to try - `deep_verify` (boolean, optional, default false): perform deep email verification - `meta_data` (object, optional) - `tags` (array of strings, optional) - `webhook_url` (string, optional) Response 200: same as `/v1/find/linkedin/single` --- #### GET /v1/find/single/status Poll the status of any single-find task. Query params: - `id` (string, required): task ID returned from the submit endpoint Response 200: same shape as submit response --- ### Single Find (Waterfall - Long Polling) These endpoints are at: https://api-waterfall.finalscout.com The connection stays open until the task completes or times out, so you do not need to poll manually. If timeout is reached, a 408 is returned with the current task status; resubmit the same request to continue. Query param (all Waterfall endpoints): - `timeout` (integer, optional, default 80): max seconds to wait, range 30-600 #### POST /find/linkedin/single (Waterfall) Same request body as `/v1/find/linkedin/single`. Response 200: task complete, returns full result Response 408: timeout, returns `{ "id": "...", "status": "Running" }` --- #### POST /find/author/single (Waterfall) Same request body as `/v1/find/author/single`. --- #### POST /find/professional/single (Waterfall) Same request body as `/v1/find/professional/single`. --- ### Bulk Find Bulk endpoints accept up to thousands of contacts per request and process them asynchronously. Use the `webhook_url` or poll `/v1/find/bulk/status` to track progress. #### POST /v1/find/linkedin/bulk Request body (JSON): - `persons` (array, required): array of `FindByLinkedinReqItem` objects (same shape as single) - `name` (string, optional): task name, max 300 characters - `duplicate` (string, optional, default "skip_duplicates"): `skip_duplicates` | `scrape_and_update_duplicates` - `tags` (array of strings, optional) - `enable_work_email` (boolean, optional, default true) - `enable_personal_email` (boolean, optional, default false) - `enable_generic_email` (boolean, optional, default false) - `webhook_url` (string, optional): receives `bulk_task.finished` and/or `contact.change` events - `webhook_subscribe_events` (array, optional): `["bulk_task.finished", "contact.change"]`; defaults to all Response 200: ```json { "id": "696a2928b47f3e1c22cca49d", "name": "My Task", "status": "Queued | Running | Completed | Failed", "total": 12, "finished": 0, "success": 0, "duplicated": 0, "credits_consumed": 0, "credits_available": 19826.5, "created_at": "2026-01-17 10:30:45" } ``` --- #### POST /v1/find/author/bulk Request body (JSON): - `persons` (array, required): array of `{ "article_url": "...", "meta_data": {} }` - `name`, `duplicate`, `tags`, `webhook_url`, `webhook_subscribe_events`: same as linkedin bulk --- #### POST /v1/find/professional/bulk Request body (JSON): - `persons` (array, required): array of `{ "full_name": "...", "domain": "...", "deep_verify": false, "meta_data": {} }` - `name`, `duplicate`, `tags`, `webhook_url`, `webhook_subscribe_events`: same as linkedin bulk --- #### GET /v1/find/bulk/status Query params: - `id` (string, required): bulk task ID Response 200: same shape as bulk submit response --- #### GET /v1/find/bulk/dump Paginated dump of all contacts found by a task. Query params: - `id` (string, required): bulk task ID - `cursor` (string, optional): pagination cursor from previous response (expires after 1 day) - `page_size` (integer, optional, default 50, range 10-100) Response 200: ```json { "cursor": "next-page-cursor-or-null", "task": { "id": "...", "status": "Completed" }, "items": [ { "contact": { ...Contact object... }, "meta_data": {} } ] } ``` --- #### GET /v1/find/bulk/export Export all contacts from a task as a CSV file. Returns a download link valid for 7 days. Query params: - `id` (string, required): bulk task ID Response 200: ```json { "status": "Generating download link | Ready", "download_url": "https://..." } ``` --- ### Account #### GET /v1/account Get account credits balance and rate limits. Response 200: ```json { "owner_email": "you@example.com", "credits_available": 19825.5, "rate_limit": [ { "api": "/find/linkedin/single", "limit": "5 per second" } ] } ``` --- ## Contact Object All endpoints that return contacts use this shape: ```json { "first_name": "Bill", "last_name": "Gates", "full_name": "Bill Gates", "avatar": "https://img.finalscout.com/people/...", "email": "bill@microsoft.com", "email_type": "Work email | Personal email | Generic email", "email_status": "Valid | Risky | Invalid | Unknown", "email_is_catchall": false, "email_score": 95, "title": "Co-chair, Bill & Melinda Gates Foundation", "location": "Seattle, Washington, United States", "company": "Breakthrough Energy", "website": "https://gatesfoundation.org", "linkedin": "https://www.linkedin.com/in/williamhgates", "industry": ["Philanthropic Fundraising Services"], "tags": [], "source": "LinkedIn Profile", "privacy": "Normal | Unsubscribed", "note": "", "created_at": "2025-11-20 14:45:32" } ``` `email_status` guidance: only send email when status is `Valid`. `email_is_catchall` means the domain accepts all incoming mail; FinalScout integrates with BounceBan to ensure catchall emails are as reliable as verified ones. `email_score` is 0-100, higher means better deliverability confidence. --- ## Webhooks Set `webhook_url` in any find request to receive push notifications instead of polling. ### Event: single_task.finished Sent when a single-find task completes (email found or not). ```json { "event_type": "single_task.finished", "task_id": "1963a064", "task_type": "single_find", "task_status": "Complete", "credits_consumed": 1, "contact": { ...Contact object... }, "meta_data": {} } ``` If no email is found, `contact` is absent and a `message` field is present instead: ```json { "event_type": "single_task.finished", "task_id": "3799b315", "task_status": "Complete", "credits_consumed": 0, "message": "Sorry, no valid email is found for this task.", "meta_data": {} } ``` ### Event: bulk_task.finished Sent when an entire bulk task completes. ```json { "event_type": "bulk_task.finished", "id": "6981d2bdaeae3b7a3117bab6", "name": "My Bulk Task", "status": "Completed", "total": 12, "finished": 12, "success": 11, "duplicated": 0, "credits_consumed": 11, "credits_available": 35124.5, "created_at": "2026-02-03 10:49:34" } ``` ### Event: contact.change Sent for each contact when an email is found during a bulk task. Not sent if no email is found. ```json { "event_type": "contact.change", "task_id": "696a2928b47f3e1c22cca49d", "task_status": "Running", "contact": { ...Contact object... }, "meta_data": {} } ``` --- ## HTTP Error Codes - 400: Invalid parameters - 401: Missing or invalid API key - 403: Insufficient credits - 405: Account is blocked - 408: Request timeout (Waterfall endpoints only) - 429: Rate limited - 500: Unexpected server error --- ## Rate Limits (default) | Endpoint | Limit | |---|---| | /find/linkedin/single | 5 per second | | /find/author/single | 5 per second | | /find/professional/single | 5 per second | | /find/single/status | 25 per second | | /find/linkedin/bulk | 2 per second | | /find/author/bulk | 2 per second | | /find/professional/bulk | 2 per second | | /find/bulk/status | 25 per second | | /find/bulk/export | 25 per second | | /account | 5 per second | Contact dev@finalscout.com to increase limits. --- ## Quick Start for AI Agents To find an email for a LinkedIn profile: 1. POST https://api.finalscout.com/v1/find/linkedin/single with `person.linkedin_url` 2. If `status` is `Running`, poll GET /v1/find/single/status?id= until `status` is `Complete` 3. Read `contact.email` from the response; only use it if `contact.email_status` is `Valid` For fastest integration, use the Waterfall endpoint at https://api-waterfall.finalscout.com/find/linkedin/single which blocks until done (set `timeout=600`). For bulk operations (100+ contacts): 1. POST /v1/find/linkedin/bulk with `persons` array and a `webhook_url` 2. Receive `contact.change` events as each email is found 3. Receive `bulk_task.finished` when the task completes