# Best influencer outreach API: accounts, email, and id (https://www.socialcrawl.dev/blog/best-influencer-outreach-api)
> Seven calls cost 48 credits. The best influencer outreach api leaves contact_email null when a bio has no address, and stores the stable YouTube channel id.
The best influencer outreach api for a growth marketer about to email someone is four calls: candidate accounts for a name, the creator's own email only when the profile text supports that choice, the YouTube about payload, and the stored channel id. Check that id before you treat a handle that fails to resolve as a removed channel.
[CreatorDB](https://creatordb.app/best-influencer-marketing-api-2026-complete-developer-guide/), [InfluenceFlow](https://influenceflow.io/resources/best-influencer-marketing-api-2026-developers-guide-to-top-platforms-1/), and [Influencer Hero](https://www.influencer-hero.com/blogs/best-influencer-marketing-api-providers), the pages ranking for this query, are discovery-database lists. A creator discovery api in that shape is a catalogue. The calls below are what you run once you already have a person or a brand name.
Those seven GETs ran on the SocialCrawl API on 2026-10-11, from 16:18:03Z to 16:18:21Z, and they cost 48 credits. The Instagram contact field came back null. The YouTube about address is also written in the biography. No rename was observed. The brief-check charge of 15 credits is still marked provisional.
## How do you find accounts with an influencer marketing api?
You pass a name, a type, and the platforms to search. On 2026-10-11 the call was `name=MrBeast`, `type=person`, `platforms=twitter,instagram,tiktok`, and `candidates=3`.
```bash
curl "https://www.socialcrawl.dev/v1/prism/find-accounts?name=MrBeast&type=person&platforms=twitter,instagram,tiktok&candidates=3" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 200, `cached` false, `credits_used` 6. Three platforms came back, three candidates each. Response order is Instagram, then TikTok, then X. The query string listed twitter, then instagram, then tiktok.
Leading handles are Instagram `mrbeast`, TikTok `mrbeast`, and X `MrBeast`. On each leader, `match.level` is `same`, `match.basis` is `judged`, and `match.handle` is the rank-1 candidate. Confidence is 0.88 on Instagram, 0.95 on TikTok, and 0.94 on X. `automated_decision` is present and false. `data._warnings` is absent, not an empty list.
The notice says: "Candidate accounts ranked from public search results, for human review. A match level is an automated label, not proof of who runs an account; your reviewer decides. Private accounts are not listed."
```json
{
"credits_used": 6,
"cached": false,
"data": {
"automated_decision": false,
"notice": "Candidate accounts ranked from public search results, for human review. A match level is an automated label, not proof of who runs an account; your reviewer decides. Private accounts are not listed.",
"platforms": [
{
"platform": "instagram",
"match": { "handle": "mrbeast", "level": "same", "basis": "judged" }
},
{
"platform": "tiktok",
"match": { "handle": "mrbeast", "level": "same", "basis": "judged" }
},
{
"platform": "twitter",
"match": { "handle": "MrBeast", "level": "same", "basis": "judged" }
}
]
}
}
```
With a known name on those three platforms, you are using this endpoint as an influencer search api. Rank 2 and rank 3 stay on the list for a person to reject.
| Platform | Rank | Handle | Followers | choice_probability |
| --- | ---: | --- | --- | ---: |
| instagram | 1 | mrbeast | null | 1 |
| instagram | 2 | mr._.beast._.104 | null | 0 |
| instagram | 3 | mr_beast_00986 | null | 0 |
| tiktok | 1 | mrbeast | 143721548 | 1 |
| tiktok | 2 | beast_clip7 | 214343 | 0 |
| tiktok | 3 | mrbeast.sigma_ | 771132 | 0 |
| twitter | 1 | MrBeast | 34987011 | 1 |
| twitter | 2 | MrBeastExtra | 386249 | 0 |
| twitter | 3 | MrBeastBurger | 190395 | 0 |
Instagram `followers` is null on all three rows. TikTok rank 1 is 143721548 followers. X rank 1 is 34987011. Instagram and TikTok `bio_excerpt` values are null. Rank 1 on X is "I want to make the world a better place". Rank 2 is "this is my place to be stupid. Main - @mrbeast". Rank 3, MrBeast Burger, is "big flavor by @mrbeast. smashed burgers, crispy chicken sandwiches, outrageous grilled cheese, seasoned crinkle fries & more. delivering nationwide."
`choice_probability` is 1 on each rank 1 and 0 on the other six rows, MrBeast Burger included. Those rows are there so a reviewer can reject them. `same` with basis `judged` is a label from public search. The payload is a candidate list for a reviewer, and `automated_decision` false leaves that decision with the person reading it.
Evidence on every leader is `website_match` false, `cross_link` false, `bio_consistent` true, and `fan_or_parody` false. `coverage` is 1. `partial_failure` is false. `context.employer`, `context.location`, and `context.website` are null. `methodology_version` is `find-accounts-v1`. LinkedIn was not requested. Private accounts are left out.
The search legs on the body are `/v1/instagram/search/profiles` at 1 credit, `/v1/tiktok/search/users` at 1 credit, and `/v1/twitter/search/users` at 1 credit. The legs sum to 3. The envelope `credits_used` is 6. The other 3 match the price checked before the call, 1 judge credit on each of the three platforms. `candidates=3` is outside that formula. The leg credits sit inside the 6. Adding them again would count the searches twice.
## When is contact_email the creator's own address, and when do you keep public_email?
The Instagram call used the official account, handle `instagram`, with `contact_email=1`.
```bash
curl "https://www.socialcrawl.dev/v1/instagram/profile?handle=instagram&contact_email=1" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 200, `credits_used` 1, `cached` false. 1 is the registry price of `instagram/profile`. This run did not also fetch the profile without the flag, so there is no measured delta for the flag. The only measured charge is 1.
`author.id` is `25025320`, the username is `instagram`, the display name is Instagram, `verified` is true, and `private` is false. `followers` is 686593882. The bio is exactly `Discover what's new on Instagram 🔎✨`, 35 characters, and it contains no email address. `business_category` is null.
`author.ext.contact_email` is present and null. `author.ext.public_email` is absent. The key is missing from the body. A missing key is a different state from a key set to null.
```json
{
"credits_used": 1,
"cached": false,
"data": {
"author": {
"id": "25025320",
"username": "instagram",
"display_name": "Instagram",
"bio": "Discover what's new on Instagram 🔎✨",
"verified": true,
"private": false,
"followers": 686593882,
"ext": {
"business_category": null,
"contact_email": null
}
}
}
}
```
`contact_email` is the creator's own address only when the text lists two or more addresses and one of them is clearly the creator's own. The field stays null for a single address, for no address, for a manager, agency, brand, or sponsor, and when the choice is unclear. On calls that already have an address, `public_email` stays the first one. No sample addresses are attached to the rule, because this run did not capture a two-address bio.
This body is the none case. `contact_email` is null, and the `public_email` key is missing. A one-address bio was not captured, and neither was a body whose `public_email` held a first address. The same is true of a manager, agency, brand, sponsor, or unclear choice. A creator email finder that supplies an address this bio does not contain is outside this call.
For the Instagram bio address alone, see [how that public email is read](/blog/instagram-email-finder). That post is Instagram public-email extraction. This one is the workflow around find-accounts, contact_email, channel about, and a stored channel id. The profile route lives on the [Instagram docs](/docs/instagram). The same parameter is legal on `youtube/channel` and `youtube/channel/about`. This harvest did not send `contact_email` on those calls, so this section stops at Instagram.
The rejected value was measured on the same handle.
```bash
curl "https://www.socialcrawl.dev/v1/instagram/profile?handle=instagram&contact_email=2" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 400, `credits_used` 0. `cached` is absent. `error.type` is `INVALID_REQUEST`. There is no `error.code`. `details.invalid_parameters` lists `contact_email`. `details.allowed_values` lists `1`. The message allows only `1`, and the response below quotes it.
```json
{
"success": false,
"error": {
"type": "INVALID_REQUEST",
"message": "Invalid value for 'contact_email': '2'. Allowed values: 1. Did you mean '1'? Example: contact_email=1.",
"details": {
"invalid_parameters": ["contact_email"],
"allowed_values": ["1"]
}
},
"credits_used": 0
}
```
## Where does the YouTube View email address come back?
On `GET /v1/youtube/channel/about` the address is `author.ext.public_email`, beside the biography and the profile links. The 2026-10-11 call used handle `mkbhd`.
```bash
curl "https://www.socialcrawl.dev/v1/youtube/channel/about?handle=mkbhd" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 200, `cached` false, `credits_used` 25, and an address was present. The registry price checked before the call is 25 when an address is returned and 0 when none is. This run did not call a channel with no address, so that 0-credit miss was not measured.
`author.ext.public_email` is `business@mkbhd.com`, a business address on `mkbhd.com`. The biography is 128 characters and already spells `business@MKBHD.com`, different case from the field. Both strings are on the same 200, so this run does not say whether the field was taken from the biography or from the View email address control.
`contact_email` was not sent. `author.ext.contact_email` is absent on this response.
Marques Brownlee, username `@mkbhd`, id `UCBJycsmduvYEL83R_U4JriQ`, is verified, joined on 2008-03-21, and is listed in the United States, with `posts_count` 1857, `total_views` 5755895689, and `followers` 21400000. `followers_approximate` is true, so 21400000 is not an exact subscriber count.
`author.ext.links` has five URLs, in this order: `https://twitter.com/MKBHD`, `https://instagram.com/MKBHD`, `https://youtube.com/c/TheStudio`, `https://reddit.com/r/MKBHD`, `https://discord.gg/MKBHD`.
The biography, as returned:
```text
MKBHD: Quality Tech Videos | YouTuber | Geek | Consumer Electronics | Tech Head | Internet Personality!
business@MKBHD.com
NYC
```
```json
{
"credits_used": 25,
"cached": false,
"data": {
"author": {
"id": "UCBJycsmduvYEL83R_U4JriQ",
"username": "@mkbhd",
"display_name": "Marques Brownlee",
"bio": "MKBHD: Quality Tech Videos | YouTuber | Geek | Consumer Electronics | Tech Head | Internet Personality!\n\nbusiness@MKBHD.com\n\nNYC",
"verified": true,
"followers": 21400000,
"posts_count": 1857,
"joined_at": "2008-03-21",
"ext": {
"country": "United States",
"public_email": "business@mkbhd.com",
"followers_approximate": true,
"total_views": 5755895689,
"links": [
"https://twitter.com/MKBHD",
"https://instagram.com/MKBHD",
"https://youtube.com/c/TheStudio",
"https://reddit.com/r/MKBHD",
"https://discord.gg/MKBHD"
]
}
}
}
}
```
Channel and channel-about routes are on the [YouTube docs](/docs/youtube).
## How do you tell a rename from a removed channel with the stored channel id?
Call A is a stored channel id. Call B is a handle this run had never resolved. Call A was not a retry of call B.
Call A:
```bash
curl "https://www.socialcrawl.dev/v1/youtube/channel?channelId=UCX6OQ3DkcsbYNE6H8uQQuVA" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 200, `cached` false, `credits_used` 1. `author.id` is `UCX6OQ3DkcsbYNE6H8uQQuVA`, the id that was sent. `author.username` is `@mrbeast`. The display name is MrBeast. A 200 on that stored id means the channel is still there. No older YouTube handle was resolved in this run, so `@mrbeast` is the current username on that id, and this run did not watch a handle change.
`author.ext.public_email` is present and null. The biography is 998 characters and contains no `@`. `youtube/channel/about` was not called for this channel, so this 1-credit response cannot tell you whether MrBeast publishes a contact address. `followers` is 521000000 with `followers_approximate` true. `posts_count` is 1006. Country is US. `joined_at` is 2012-02-20.
```json
{
"credits_used": 1,
"cached": false,
"data": {
"author": {
"id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
"username": "@mrbeast",
"display_name": "MrBeast",
"followers": 521000000,
"posts_count": 1006,
"joined_at": "2012-02-20",
"ext": {
"country": "US",
"public_email": null,
"followers_approximate": true
}
}
}
}
```
Call B, the handle that never resolved here:
```bash
curl "https://www.socialcrawl.dev/v1/youtube/channel?handle=scghostxq7k2m9pnotachannel" \
-H "x-api-key: $SOCIALCRAWL_API_KEY"
```
HTTP 404, `credits_used` 0. `cached` is absent. `error.type` is `RESOURCE_NOT_FOUND`. `error.details.reason` is `account_gone`. The message says the request was not charged. The suggestion says channel ids stay put when handles change. Call `/v1/youtube/channel` with `channelId` set to the `author.id` you already hold, the id starting with UC. A 200 on that retry carries the new handle in `author.username`. A second 404 means the channel is gone. The response below quotes the suggestion.
```json
{
"success": false,
"error": {
"type": "RESOURCE_NOT_FOUND",
"message": "The platform reports no account under this identifier. It has been removed or deactivated, the name was changed, or it never existed. You were not charged for this request.",
"details": {
"reason": "account_gone",
"suggestion": "YouTube channel ids are permanent while handles are not. Call `/v1/youtube/channel` with `channelId` set to the `author.id` you already hold (the id starting with UC): a 200 means the channel changed its handle and the response carries the new one in `author.username`, and a second 404 means the channel is genuinely gone."
}
},
"credits_used": 0
}
```
`scghostxq7k2m9pnotachannel` was never a live channel in this run, so `account_gone` here means the platform did not resolve that string. The 200 above is a different input. This run did not observe a rename, did not hold both URLs across [14 days](https://support.google.com/youtube/answer/15920820), and did not send a second 404 against a previously live id. YouTube's [`channels.list`](https://developers.google.com/youtube/v3/docs/channels/list) quota of 1 unit is a YouTube counter, not a SocialCrawl credit. The SocialCrawl credit on these two calls was 1 on the 200 and 0 on the 404.
## When is a brief-check worth calling before you send?
Only when you already have a public URL and the points you need covered. It is optional. Skip it and the four calls above are still the send path. Nothing in this call was posted, edited, or taken down.
The call that ran used one public YouTube URL and two points. `forbidden` was not sent. `require_disclosure` was not sent.
```bash
curl -G "https://www.socialcrawl.dev/v1/prism/brief-check" \
-H "x-api-key: $SOCIALCRAWL_API_KEY" \
--data-urlencode "url=https://www.youtube.com/watch?v=jNQXAC9IVRw" \
--data-urlencode "points=Mention the elephants,Describe the animals"
```
HTTP 200, `cached` false, envelope `credits_used` 15. `no_transcript` is false. `verdict` is `pass`. `forbidden_hits` is an empty list. `disclosed` is false. `require_disclosure` was not sent, so that false value was not scored as a disclosure check.
The levels are 2.26 for "Mention the elephants" and 2.07 for "Describe the animals". Both quotes are the same cut-off string. The response stops at `really...`, and the block below copies the quote without finishing it.
```json
{
"credits_used": 15,
"cached": false,
"data": {
"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
"points": [
{
"point": "Mention the elephants",
"level": 2.26,
"quote": "Me at the zoo All right, so here we are, in front of the elephants the cool thing about these guys is that they have really..."
},
{
"point": "Describe the animals",
"level": 2.07,
"quote": "Me at the zoo All right, so here we are, in front of the elephants the cool thing about these guys is that they have really..."
}
],
"forbidden_hits": [],
"disclosed": false,
"no_transcript": false,
"verdict": "pass"
}
}
```
The legs `/v1/youtube/video` and `/v1/youtube/video/transcript` each returned HTTP 200 with `credits_used` 0. The billed amount is the envelope 15. Leave the leg zeros out of that 15. `verdict` `pass` covers these two points on this one URL. `no_transcript` false means the call had a transcript. The registry description still says "15 credits per post (provisional)." This call was charged 15. The two figures match on this run, and the price stays provisional. The route is on the [Prism docs](/docs/prism).
## How do you start using this?
Get a SocialCrawl key. No signup grant was measured, so none is listed here. Put the key in `$SOCIALCRAWL_API_KEY` and send it on the `x-api-key` header. One API key covers the seven GETs.
The first call is the find-accounts request above. Then send the other six query strings: Instagram profile with `contact_email=1`, the same profile with `contact_email=2`, `youtube/channel/about` for `mkbhd`, `youtube/channel` with the stored channel id, the ghost handle, and the brief-check URL if you want that optional step. Read `credits_used` on each body. Call `GET /v1/credits/balance` before the batch and again after.
If your influencer data api stores a YouTube row by handle, also store `author.id` from the 1-credit channel call. An influencer outreach tool is a seat you log into, and these calls stop before anyone sends mail. Wiring this best influencer outreach api into a review queue starts with the find-accounts GET.
### What was measured on 2026-10-11?
The window is 2026-10-11T16:18:03Z through 2026-10-11T16:18:21Z. The seven data calls were sent together, so `credits_remaining` on those bodies is not a sequence. Spend is 48. The arithmetic is `6+1+0+25+1+0+15`. The balance moved from 38660 to 38612, the same 48. `recent_deductions` moved from 997 to 1003, a delta of 6, and that delta is not the spend.
Give `youtube/channel/about` an HTTP timeout of at least 300 seconds. This run returned inside that short batch window. A first read of an address can take longer.
From there, open the visual explorer, then the docs for [Prism](/docs/prism), [Instagram](/docs/instagram), [YouTube](/docs/youtube), and [endpoint pricing](/docs/endpoint-pricing). Check the 6, the 1, the 0, the 25, the 1, and the 15 against that pricing page. Keep the 15 provisional.
## What should you confirm before you send?
### Does find-accounts verify that an account is theirs, or is it a list for a person to review?
It returns a list for a person to review. On the MrBeast call, `automated_decision` is false. The three leaders are `match.level` `same` and `match.basis` `judged`, and `data._warnings` is absent. The notice says the level is a label and the reviewer decides. Rank 2 and rank 3 stay on the list, including MrBeast Burger, with `choice_probability` 0 against 1 on each leader.
### Why does contact_email stay empty when the bio has only one address?
`contact_email` stays null for one address, for none, for a manager, agency, brand, or sponsor, and when the choice is unclear. The field fills only when the text lists two or more addresses and one is the creator's own. This run is the none case. The official Instagram bio has no email, `contact_email` is present and null, and the call used 1 credit. A one-address bio was not captured. `contact_email=2` on the same handle returned HTTP 400 at 0 credits, `error.type` `INVALID_REQUEST`, with the only allowed value set to 1.
### How is contact_email different from public_email?
On that Instagram body, `contact_email` is present and null, and `public_email` is not a key on the body. A missing key is a different state from a key set to null. `public_email` stays the first address on calls that have one. `contact_email` fills only when two or more addresses are listed and one is the creator's own. This run did not return a filled Instagram `public_email`. The mkbhd about call has `public_email` `business@mkbhd.com` and did not send `contact_email`, so `contact_email` is absent there.
### Where does YouTube return the address behind View email address?
On `GET /v1/youtube/channel/about?handle=mkbhd`, `author.ext.public_email` is `business@mkbhd.com`, HTTP 200, 25 credits. The biography is 128 characters and already spells `business@MKBHD.com`, and `author.ext.links` has five URLs. This run does not separate the View email address control from the biography. Both strings sit on the same response. A no-address about call, the registry's 0-credit case when no address comes back, was not measured. The 1-credit `youtube/channel` call is a different endpoint. On channel id `UCX6OQ3DkcsbYNE6H8uQQuVA`, `public_email` is null, and channel about was not called for that channel.
### How do you tell a renamed YouTube channel from one that is gone?
A handle the platform does not resolve returns HTTP 404, reason `account_gone`, at 0 credits. The suggestion says to call `/v1/youtube/channel` again with `channelId` set to the stored `author.id`, the id that starts with UC. A 200 means the channel is still there and `author.username` is the current handle. A second 404 means the channel is gone. In this harvest the 200 used `channelId=UCX6OQ3DkcsbYNE6H8uQQuVA`. `author.id` is that same id, `author.username` is `@mrbeast`, and the call cost 1 credit. The 404 is handle `scghostxq7k2m9pnotachannel`, a string that was never a live channel here. No rename was observed. The two calls are different inputs, so there is no before-and-after handle to copy from them.
### Is brief-check required, and is its credit price final?
Brief-check is optional, and the 15-credit price is still provisional. The call that ran used one public YouTube URL and two points, returned HTTP 200, and charged 15 credits. `no_transcript` is false, `verdict` is `pass`, and the levels are 2.26 and 2.07. The registry description still says "15 credits per post (provisional)." The legs `/v1/youtube/video` and `/v1/youtube/video/transcript` each show `credits_used` 0. Leave those zeros out of the 15. Skip the call when you do not already have a public URL and the points you need covered.