A social posts API returns what a person or company published recently. That is a signal. It is not a contact record, and you still need to know who the person is and how to reach them.
LeadOcean does not return posts. It is the people and company layer: who to watch, and how to reach them. This guide builds the list in LeadOcean and shows where a posts source plugs in.
TL;DR
- Build the watch list with LeadOcean search, enrich each person once, and hand their LinkedIn URL to a posts API. Setup takes about 45 minutes.
- Sizing and testing spend 0 records. Each person enriched spends 1 record, so the walkthrough below spends 3.
- Read the post first, then the
email_status. Send only when both hold.
Prerequisites
- A LeadOcean account on the Free plan: 1,000 records for life, no card, 5 requests a second. Register at app.leadocean.io.
- An API key from the app, stored as
$LEADOCEAN_API_KEY. curl,jqand Python 3 withrequests.- An account with a social posts API. This guide uses Crustdata's posts API as the example. Its page (September 2026) lists posts by person, posts by company and posts by keyword, with likes, comments and shares.
Step 1: Decide what each source answers
Posts answer "what is this person talking about this week". LeadOcean answers "who is this and can I email them". Keep the two jobs apart.
| Question | Source |
|---|---|
| Who are the VPs of sales at my target accounts? | LeadOcean people search |
| Is their email sendable? | LeadOcean email_status |
| What did they post this week? | A posts API |
| Where did they work before? | LeadOcean resume_data |
One honest caveat. LeadOcean search has a keywords filter, but it matches the person's own profile text: headline, summary, skills and job descriptions. That is not their posts.
Step 2: Size the watch list for free
Count before you spend. A count costs 0 records. Send count=true as a query parameter, not in the JSON body.
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"keywords": ["outbound"],
"country": ["US"],
"limit": 1
}' | jq '.meta | {total, totalIsExact, countOnly}'Enum values are exact strings. VP works. Vice President returns nothing. Check GET /v1/enums/job_level if unsure.
Inside an agent, the free leadocean_count_leads MCP tool does the same.
Step 3: Pull the people
Search the same filters without count. Each person returned spends 1 record, so keep limit small while testing.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"keywords": ["outbound"],
"country": ["US"],
"limit": 3
}' > people.json
jq -r '.data[].meta.source_ids.person_id' people.jsonThree rows spend 3 records. The person_id is what you enrich by next. For more rows, page with the cursor from meta.nextCursor.
Step 4: Enrich and get the profile URL
Posts APIs key on a profile. Enrich by person_id to get the full record.
PID=$(jq -r '.data[0].meta.source_ids.person_id' people.json)
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d "{\"person_id\": \"$PID\", \"reveal_email\": true}" > lead.json
jq -r '.data.profile_data.profile_url' lead.jsonThe LinkedIn URL is profile_data.profile_url (enrich docs). Search rows carry the same field, so you can read it from people.json and enrich only the people you will write to. About one person in five has no LinkedIn URL; skip those for a posts lookup.
Enrichment spends 1 record. A person we hold nothing for is a 404 and spends nothing.
Step 5: Fetch the posts
Send the profile URL to your posts provider. The call below is a placeholder: take the real path, parameters and auth from the provider's docs, not from this page.
import os, requests
POSTS_URL = os.environ["POSTS_API_URL"] # from your posts provider's docs
POSTS_KEY = os.environ["POSTS_API_KEY"]
def recent_posts(profile_url):
r = requests.get(
POSTS_URL,
headers={"authorization": f"Bearer {POSTS_KEY}"},
params={"profile_url": profile_url}, # parameter name: check the provider's docs
timeout=30,
)
r.raise_for_status()
return r.json()Crustdata's posts page (September 2026) says its API returns post text, hyperlinks and engagement counts. It names tracking hiring updates and founder announcements as use cases. It lists no price on that page, so confirm cost in their dashboard before you loop over a list.
Step 6: Join, filter and decide
Join on the LinkedIn URL. Keep a lead only when it has a recent post that matches your trigger and a sendable email.
import json
lead = json.load(open("lead.json"))["data"]
profile_url = lead["profile_data"].get("profile_url")
status = lead["contact_data"].get("email_status") or "none"
role = (lead["contact_data"].get("contact_current_experiences") or [{}])[0]
posts = recent_posts(profile_url)
hits = [p for p in posts.get("data", []) if "outbound" in json.dumps(p).lower()]
sendable = status in {"verified", "catch_all_valid", "catch_all"}
print({"role": role, "status": status, "post_hits": len(hits), "send": bool(hits) and sendable})Two rules matter here. A post is a reason to write, not permission to quote it back at the person. And LeadOcean gives no refund for bounced emails, so read email_status first.
Dataset records are refreshed monthly and each carries a fetched_at date. A job title can lag a post by weeks. If the post says "excited to join", trust the post over the title.
What you get
Step 2 ran through the free leadocean_count_leads tool on 2026-10-21. Filters: jobLevel VP, jobFunction Sales & Business Development, keywords "outbound", country US.
{"total":510,"totalIsExact":true,"totalAll":997,"distinctCompanies":473,"credits":0}That is a mailable count: the tool defaults to verified, catch_all_valid and catch_all emails. totalAll is the same filters with no email condition. The call spent 0 records.
An enrich response has this shape. Response shape from the API reference (placeholder values):
{
"data": {
"profile_data": { "profile_full_name": "Jane Doe", "profile_url": "https://www.linkedin.com/in/jane-doe" },
"contact_data": {
"has_email": true,
"email_status": "verified",
"contact_emails": [
{ "email": "jane.doe@acme.com", "type": "work", "status": "verified", "priority": 1 }
],
"contact_current_experiences": [ { "job_title": "VP Sales", "company_name": "Acme" } ]
},
"resume_data": { "experiences": [], "educations": [] }
},
"meta": { "credits": 1 }
}The posts side of the join comes from your posts provider, so its shape is theirs.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked key | Re-copy the key from app.leadocean.io. The header is x-api-key. |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Wait for Retry-After, then retry. |
| 402 | Fewer records left than the request needs | Check GET /v1/account, or move to Solo or Agency. |
| Empty search results | A filter value outside the list, such as Vice President | Use VP. Check GET /v1/enums/job_level. |
| 404 on enrich | Nobody matched, or no enrichable identifier is held | Costs nothing. Skip the lead or try another person. |
| Search stops paging | One search pages to 10,000 rows (meta.depthCapped) | Narrow the filters instead of walking further. |
FAQ
Does LeadOcean have a social posts API?
No. LeadOcean returns people and company records, with search, enrichment, email, phone and career history. Use a posts provider for posts and join on the profile URL.
What does this cost in LeadOcean records?
Counts spend 0. Search spends 1 record per person returned. Enrich spends 1 per person found. Free has 1,000 records for life. Solo is $97 a month and Agency is $497 a month, with a fair-use pace instead of credits. See pricing.
Is the data real time?
The posts side is the provider's. LeadOcean is refreshed monthly. For a live call per lead, see people data enrichment in real time.
Can an AI agent run this?
Yes. The hosted MCP server has 13 tools, including free counting. See real-time B2B data for AI agents. For web results as a signal, see the SERP API guide. More guides are on the blog.
Build the people side of your post signals free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →