The SourceWhale Public API allows you to programmatically interact with your SourceWhale account, adding contacts, triggering campaigns, querying statistics, and more.
📖 Interactive Documentation
Explore all endpoints and try them live at: https://sourcewhale.app/public-api/swagger
Authentication
Every API request must include your API key in the request header:
API-Key: your_api_key_here
To generate your API key:
Go to your SourceWhale dashboard and click your profile icon in the bottom left corner.
Click Admin, then Settings, then click Generate API Key.
⚠️ Admin access is required to generate an API key.
Base URL
https://sourcewhale.app/public-api
Contacts
Add Contacts: POST /v1/candidates/add
Add one or more contacts to SourceWhale. Optionally enrol them directly into a campaign.
candidates(required): Array of contact objects. Each contact can includefirstName,lastName,email,phone,socialLinks, and more.campaignId(optional): Enrol contacts into a specific campaign.projectIds(optional): Add contacts to one or more projects.sendImmediately(optional, boolean): Iftrueand acampaignIdis provided, outreach sends immediately without review.userEmail(optional, query param): Assign contacts to a specific user by email. Defaults to an admin user.
Response: Returns a url and a list of candidateIds.
Example request:
POST https://sourcewhale.app/public-api/v1/candidates/add{
"candidates": [
{
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@example.com",
"phone": "+447911123456"
}
],
"campaignId": "your_campaign_id_here"
}
Search Contacts: GET /v1/candidates/search
Look up existing contacts by a specific identifier.
key(required): Field to search by:email,phone,photo, orsocialLink.value(required): The value to search for.userEmail(optional): Restrict results to a specific user.
⚠️ Important: You must pass key and value as two separate query parameters. Passing only the field name (e.g. ?email=...) will cause an error.
Example requests:
GET https://sourcewhale.app/public-api/v1/candidates/search?key=email&value=jane.smith@example.com
GET https://sourcewhale.app/public-api/v1/candidates/search?key=phone&value=+447911123456
GET https://sourcewhale.app/public-api/v1/candidates/search?key=socialLink&value=https://linkedin.com/in/janesmith
Response: Returns a list of matching contact objects.
Update a Contact: POST /v1/candidates/modify
Update the fields of an existing contact.
candidate(required): Object containingcandidateIdplus the fields to update.
Response: Returns "success" on completion.
Example request:
POST https://sourcewhale.app/public-api/v1/candidates/modify{
"candidate": {
"candidateId": "your_candidate_id_here",
"email": "updated.email@example.com",
"phone": "+447911000000"
}
}
Campaigns
List Campaigns: GET /v1/campaigns/list
Retrieve all live campaigns for your team.
userEmail(optional): Filter to campaigns created by a specific user.includeMetrics(optional,true/false): Include open rate, reply rate, interest rate, and booked rate.
Response: Returns a list of campaigns with campaignId and campaignName.
Example requests:
GET https://sourcewhale.app/public-api/v1/campaigns/list
GET https://sourcewhale.app/public-api/v1/campaigns/list?userEmail=recruiter@yourcompany.com&includeMetrics=true
Projects
List Projects: GET /v1/projects/list
Retrieve all projects for your team.
userEmail(optional): Filter to projects created by a specific user.
Response: Returns a list of projects with projectId and projectName.
Example requests:
GET https://sourcewhale.app/public-api/v1/projects/list
GET https://sourcewhale.app/public-api/v1/projects/list?userEmail=recruiter@yourcompany.com
Statistics
Dashboard Statistics: GET /v1/statistics/dashboard
Retrieve outreach statistics for your team over a date range.
from(required): Start date inYYYY-MM-DDformat.to(required): End date inYYYY-MM-DDformat.timezone(optional): Timezone string (e.g.Europe/London). Defaults toUTC.
Response: Returns counts for sourced, sent, opened, replied, interested, and booked across the date range.
Example requests:
GET https://sourcewhale.app/public-api/v1/statistics/dashboard?from=2026-01-01&to=2026-03-31
GET https://sourcewhale.app/public-api/v1/statistics/dashboard?from=2026-01-01&to=2026-03-31&timezone=America/New_York
GET https://sourcewhale.app/public-api/v1/statistics/dashboard?from=2026-01-01&to=2026-03-31&timezone=Europe/London
Webhooks
Subscribe to Events: POST /v1/zapier/subscribe
Register a webhook URL to receive real-time notifications when contacts are added or updated in SourceWhale.
Request body:
url(required): The webhook URL to receive event payloads.subscriptionType(required): The event type to subscribe to. Accepted values:candidateCreated: fires when a new contact is added to SourceWhale for the first time.candidateUpdated: fires when an existing contact's data is modified.
triggerKeys(optional, array,candidateUpdatedonly): Limit which fields trigger the webhook. For example,["email", "phone"]will only fire when those specific fields change. If omitted, any field change triggers the webhook.
Response: Returns an id (subscriptionId) for this webhook registration.
Example: trigger when a new contact is added:
POST /v1/zapier/subscribe {"url": "https://your-webhook-url.com/endpoint", "subscriptionType": "candidateCreated"}
Example: trigger only when a contact's email or phone changes:
POST /v1/zapier/subscribe { "url": "https://your-webhook-url.com/endpoint", "subscriptionType": "candidateUpdated", "triggerKeys": ["email", "phone"]}
The webhook payload will contain { "candidate": { ...contactObject } }.
Common Issues and FAQs
"Header name must be a valid HTTP Token" error
This error means your API key header name contains an invalid character, most commonly a space. HTTP header names cannot contain spaces.
Fix: Make sure the header is named exactly API-Key (with a hyphen, not a space). In Postman, go to the Headers tab and check the header name.
Incorrect:
API Key(space)Correct:
API-Key(hyphen)
Getting a 502 error on the Search Contacts endpoint
A 502 on GET /v1/candidates/search is usually caused by missing query parameters. The endpoint requires both key and value as separate parameters.
Fix: Make sure you are passing two separate parameters, not just the field name on its own.
Incorrect:
?email=jane@example.comCorrect:
?key=email&value=jane@example.com
In Postman, set these up in the Params tab as two separate rows:
Key:
key, Value:emailKey:
value, Value:jane@example.com
My API key is not working
Check the following:
The header name is
API-Key(hyphen, not space, notAuthorization).You are using the key generated for your team. Only admin users can generate API keys (via Admin > Settings > Generate API Key).
The key has not been regenerated. Generating a new key invalidates the previous one.
What date format should I use for statistics queries?
Dates must be in YYYY-MM-DD format (e.g. 2026-01-31). Other formats such as 01/31/2026 or 31-01-2026 will not work.
What does the optional userEmail parameter do?
Several endpoints accept a userEmail query parameter. When provided, results are scoped to that user only (e.g. only their campaigns, projects, or contacts). If omitted, the request returns data for the whole team.
What values can I use for the key parameter on Search Contacts?
The key parameter accepts one of four values:
email: search by email addressphone: search by phone numberphoto: search by profile photo URLsocialLink: search by social profile URL (e.g. LinkedIn)
Stuck or need some help? Click on the chat icon at the bottom right-hand corner to connect with our support team!