Developers
API documentation
Read your LinkedIn content, write and schedule posts, manage your engagement feeds, and drive outreach campaigns from your own scripts and automations.
https://myfeedin.co/api/v1Introduction
The MyFeedIn API is a REST API over HTTPS. It returns JSON, uses standard HTTP status codes, and is authenticated with an API key. Every request is scoped to the account or workspace that key belongs to, so a key never sees data outside its scope.
Create and manage keys in Settings, API keys.
Prefer chatting with your data from Claude instead of writing code? Connect the MyFeedIn MCP server, it exposes the same posts, analytics, feeds and campaigns as conversational tools, including adding profiles to a campaign by URL and rendering your stats as a shareable image.
OpenAPI spec
The whole API is described in an OpenAPI 3.1 file. Import it in Postman or Insomnia, generate a client, or hand it to an AI agent so it knows every endpoint and parameter.
https://myfeedin.co/api/v1/openapi.jsonAuthentication
Pass your key as a bearer token on every request. Keys are shown once at creation and stored hashed, so keep yours somewhere safe and never expose it in client-side code.
curl https://myfeedin.co/api/v1/me \
-H "Authorization: Bearer mfi_live_your_key_here"A missing or invalid key returns 401. An inactive subscription returns 402.
Scopes
Each key grants a chosen subset of permissions. The campaign scopes require an active Growth plan, checked at request time. Feed scopes work on every plan, except adding people to a feed, which also needs Growth.
| Field | Type | Description |
|---|---|---|
| posts:read | scope | Read your published LinkedIn posts and their analytics. Analytics sit here rather than under a scope of their own because they are sums of the posts this scope already returns. |
| posts:write | scope | Write drafts and schedule them on LinkedIn through the Content calendar. Not granted by default: tick it when you create the key. Scheduling also needs a connected LinkedIn account and a plan that allows it. |
| comments:read | scope | Read the comments you left on other people's posts. |
| feeds:read | scope | List your feeds and the people in them. |
| feeds:write | scope | Create feeds, remove people, and add people by URL (adding requires Growth). |
| campaigns:read | scope | List campaigns and read their stats. Requires Growth. |
| campaigns:write | scope | Create campaigns and add profiles. Requires Growth. |
Rate limits
Requests are rate limited per key. When you exceed the limit the API returns 429 with a retry_after value in seconds. Back off and retry after that window.
Errors
Errors return a JSON body of the shape { "error": "message" } with a conventional status code.
| Field | Type | Description |
|---|---|---|
| 400 | Bad Request | Missing or malformed parameters. |
| 401 | Unauthorized | Missing, invalid, or revoked key. |
| 402 | Payment Required | Subscription inactive. |
| 403 | Forbidden | Key lacks the required scope or plan. |
| 404 | Not Found | Resource does not exist in this scope. |
| 409 | Conflict | The resource's state does not allow this action (e.g. editing a published post). |
| 422 | Unprocessable | Validation failed. The body adds a code and, when useful, a problems list. |
| 429 | Too Many Requests | Rate limit exceeded. |
Personal keys and workspace keys
A key is bound to one scope when you create it: either your personal account, or one workspace. That binding is permanent. To switch, revoke the key and mint a new one. GET /v1/me tells you which kind you are holding.
The difference matters because MyFeedIn stores two kinds of data. Content belongs to a person: your posts and your comments carry your user id, and a workspace never owns them. Everything else, feeds, saved people and campaigns, belongs to the workspace.
So a workspace key reads the posts, comments and analytics of every member, not just the person who created the key. Each row then carries an author naming who it belongs to, and the member parameter narrows a request to one person. A personal key reads exactly one account and never returns author.
| Field | Type | Description |
|---|---|---|
| member | string | Accepted by /v1/posts, /v1/posts/{id}, /v1/comments and both analytics endpoints. A member's name, email or id: call /v1/workspace/members to list them. Omit to cover every member. Passing it on a personal key returns 400. |
| author | object | Present on every post and comment when the scope spans more than one person: { user_id, name }. |
curl "https://myfeedin.co/api/v1/posts?member=John&limit=5" \
-H "Authorization: Bearer mfi_live_your_key_here"Account
Current key
Returns the calling key's user id, scope, and granted scopes. The simplest way to confirm a key works.
{
"user_id": "a418d3f9-…",
"workspace_id": null,
"scope": "personal",
"scopes": ["posts:read", "campaigns:read", "campaigns:write"]
}Posts
List posts
Published LinkedIn posts. Newest first by default; pass sort to rank by engagement instead, and q to search the post body. Requires posts:read.
| Field | Type | Description |
|---|---|---|
| q | string | Case-insensitive search across the post body. |
| sort | string | recent (default), impressions, comments or reactions. Combines with q to rank the matches. |
| period | string | 30d, 90d, 365d or all. Defaults to 90d for ranked sorts, unbounded otherwise. |
| since | string | Earliest published_at, ISO 8601. |
| until | string | Latest published_at, ISO 8601. |
| member | string | Workspace keys only. Restrict to one member. |
| limit | integer | 1 to 100. Default 20. |
Workspace keys
author. Narrow with member.curl "https://myfeedin.co/api/v1/posts?q=hiring&sort=impressions&period=365d" \
-H "Authorization: Bearer mfi_live_your_key_here"{
"count": 1,
"posts": [
{
"id": "9ba6e7c7-…",
"url": "https://www.linkedin.com/posts/…",
"num_likes": 24,
"num_comments": 5,
"num_impressions": 1330,
"num_shares": 3,
"published_at": "2026-06-17T06:58:07Z",
"excerpt": "I posted the exact same article on X and LinkedIn…",
"author": { "user_id": "6938a74e-…", "name": "John Schapmann" }
}
]
}Get a post
One post with its full body and media arrays. The list endpoint returns a 500-character excerpt to keep payloads small; this returns everything. Requires posts:read.
Workspace keys
404.curl https://myfeedin.co/api/v1/posts/POST_ID \
-H "Authorization: Bearer mfi_live_your_key_here"Analytics
Aggregate analytics
Totals and averages across the period: post count, likes, comments, impressions, shares, and an average-engagement figure. Requires posts:read, since these are sums of the posts that scope already returns.
| Field | Type | Description |
|---|---|---|
| period | string | 30d (default), 90d, 365d or all. |
| compare_to | string | Pass 'previous' to also return the preceding window and the percent deltas. Not available with period=all. |
| member | string | Workspace keys only. Restrict to one member. |
Workspace keys
by_member array, so one member publishing nothing stays visible instead of being averaged away.{
"period": "30d",
"post_count": 14,
"total_impressions": 41230,
"avg_engagement": 38.5,
"by_member": [
{ "member": { "user_id": "a418d3f9-…", "name": "Axel Schapmann" }, "post_count": 14, "total_impressions": 41230 },
{ "member": { "user_id": "6938a74e-…", "name": "John Schapmann" }, "post_count": 0, "total_impressions": 0 }
]
}Engagement patterns
Best day of week, best hour of day, and average engagement per post length bucket, computed from your own history rather than generic advice. Requires posts:read.
| Field | Type | Description |
|---|---|---|
| period | string | 30d, 90d, 365d (default) or all. Short windows produce confident-looking noise. |
| timezone | string | IANA zone the best day and hour are reported in, e.g. Europe/Paris. Default UTC; the response echoes the zone applied. |
| member | string | Workspace keys only. Restrict to one member. |
Workspace keys
members_pooled so you know how many went into the sample; pass member for an individual.Comments
List comments
Comments left on other people's posts, newest first, each with context about the post it was left under. Requires comments:read, a separate scope from posts:read because this is a different dataset: what you wrote under other people's content.
| Field | Type | Description |
|---|---|---|
| q | string | Case-insensitive search across the comment body. |
| since | string | Earliest comment date, ISO 8601. |
| member | string | Workspace keys only. Restrict to one member. |
| limit | integer | 1 to 100. Default 20. |
Each comment carries commented_at (ISO 8601), permalink (the comment on LinkedIn) and post_url (the post it was left under), with LinkedIn tracking parameters removed.
Workspace keys
author.curl "https://myfeedin.co/api/v1/comments?q=pricing&limit=10" \
-H "Authorization: Bearer mfi_live_your_key_here"People
Search saved people
Search everyone saved across your feeds and lists by name or headline, without knowing which feed they sit in. Requires feeds:read.
| Field | Type | Description |
|---|---|---|
| q | string | Required. Name or headline keyword. |
| limit | integer | 1 to 100. Default 20. |
Each person carries linkedin_url (their profile) and picture_url (their avatar). profile_url is an older alias of the avatar, kept for existing integrations.
Workspace keys
member parameter here.Workspace
List workspace members
Who the key can read, and how to name them in the member parameter. No scope required. Emails are not returned: member matches on name or id.
Workspace keys
200 with an empty roster rather than an error, so one code path handles both kinds of key.{
"scope": "workspace",
"workspace_id": "aa22756a-…",
"count": 2,
"members": [
{ "id": "a418d3f9-…", "name": "Axel Schapmann", "role": "owner", "post_count": 841, "is_key_owner": true },
{ "id": "6938a74e-…", "name": "John Schapmann", "role": "editor", "post_count": 48, "is_key_owner": false }
]
}Feeds
List feeds
Your engagement feeds, newest first, each with a member count. Requires feeds:read.
curl https://myfeedin.co/api/v1/feeds \
-H "Authorization: Bearer mfi_live_your_key_here"{
"count": 2,
"feeds": [
{
"id": "7f2c9a10-…",
"name": "SaaS founders",
"description": "People I want to see every morning",
"is_public": false,
"member_count": 24,
"people_count": 24,
"created_at": "2026-05-02T09:12:44Z"
},
{
"id": "b81d4e63-…",
"name": "Clients",
"description": null,
"is_public": false,
"member_count": 9,
"people_count": 9,
"created_at": "2026-03-18T14:03:21Z"
}
]
}Create feed
Creates an empty feed. Requires feeds:write. Fill it from the app, the Chrome extension, or the add-people endpoint below (Growth plan).
| Field | Type | Description |
|---|---|---|
| name | string | Feed name. Required, max 120 characters. |
curl -X POST https://myfeedin.co/api/v1/feeds \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"SaaS founders"}'{
"feed": {
"id": "7f2c9a10-…",
"name": "SaaS founders",
"member_count": 0,
"created_at": "2026-07-20T10:04:12Z"
}
}List people in a feed
The people in one feed. Use the returned person ids with the remove endpoint below. Requires feeds:read.
| Field | Type | Description |
|---|---|---|
| limit | integer | 1 to 200. Default 100. |
curl "https://myfeedin.co/api/v1/feeds/FEED_ID/people?limit=50" \
-H "Authorization: Bearer mfi_live_your_key_here"{
"feed": { "id": "7f2c9a10-…", "name": "SaaS founders" },
"count": 1,
"people": [
{
"id": "c3f81b52-…",
"full_name": "Jane Doe",
"headline": "Founder at Acme",
"linkedin_url": "https://www.linkedin.com/in/jane-doe",
"picture_url": "https://media.licdn.com/dms/image/…"
}
]
}Add people to a feed
Adds LinkedIn profiles to a feed by URL. Each URL is resolved through your connected LinkedIn account to its real LinkedIn identifier (a feed member without one would never appear in the feed), created in your CRM if new, then attached to the feed. Requires feeds:write, the Growth plan, and a connected LinkedIn account. Keys on other plans get a 403; without a connected account the API returns 400.
| Field | Type | Description |
|---|---|---|
| linkedin_url | string | A single profile URL. |
| linkedin_urls | string[] | Up to 25 profile URLs. Use instead of linkedin_url for batches. |
curl -X POST https://myfeedin.co/api/v1/feeds/FEED_ID/people \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"linkedin_urls":["https://www.linkedin.com/in/jane-doe"]}'{
"added": 1,
"created": 1,
"matched": 0,
"already_in_feed": 0
}created counts people new to your CRM, matched counts people you already had. Someone already in the feed is skipped and counted in already_in_feed. A URL that cannot be resolved (private or out of network) is reported in an errors array without failing the rest of the batch.
Remove person from feed
Removes a person from the feed. The person stays in your CRM and in any other feed they belong to; only this membership is deleted. The personId comes from the list endpoint above. Requires feeds:write.
curl -X DELETE https://myfeedin.co/api/v1/feeds/FEED_ID/people/PERSON_ID \
-H "Authorization: Bearer mfi_live_your_key_here"{ "removed": true }Publishing
List calendar posts
Posts written in the MyFeedIn Content calendar: drafts, scheduled posts with their slot, failed ones with the reason, and posts published through MyFeedIn in the last 60 days. This is not your LinkedIn history: for that, use /v1/posts. Requires posts:write.
| Field | Type | Description |
|---|---|---|
| status | string | all (default), draft, scheduled, published or failed. |
| since | string | On or after this slot or publication date, YYYY-MM-DD or ISO 8601. |
| until | string | On or before this slot or publication date. |
| limit | integer | 1 to 200. Default 50. |
Workspace keys
author.Create a draft
Saves a post as a draft. Nothing reaches LinkedIn until you schedule it. Plain text, line breaks as \n; the first link in the text becomes the preview. Media is added from the editor: the response carries editor_url. Requires posts:write.
| Field | Type | Description |
|---|---|---|
| text | string | Required. The post. |
| first_comment | string | Posted under the post about 2 minutes after it goes live. |
| scheduled_at | string | Places the draft on a day without scheduling it. Same format as the schedule endpoint. |
| timezone | string | IANA zone for a local scheduled_at. Defaults to Settings, Publishing. |
curl -X POST https://myfeedin.co/api/v1/drafts \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"text":"Three things I learned shipping our API this week..."}'{
"post": {
"id": "5c1e0b7a-…",
"status": "draft",
"text": "Three things I learned shipping our API this week...",
"first_comment": null,
"scheduled_at": null,
"editor_url": "https://myfeedin.co/dashboard/content/5c1e0b7a-…"
}
}Get a calendar post
One calendar post with its full text. Requires posts:write.
Edit a draft
Changes the text, the first comment or the day of a draft, scheduled or failed post. Pass only what changes. A scheduled post stays scheduled and the edited version is what goes out. Published posts cannot be edited (409). Requires posts:write.
| Field | Type | Description |
|---|---|---|
| text | string | New text. |
| first_comment | string | New first comment. An empty string or null removes it. |
| scheduled_at | string | Moves the draft's day. To reschedule a scheduled post, use the schedule endpoint. |
| timezone | string | IANA zone for a local scheduled_at. |
Schedule a post
MyFeedIn publishes the post on your LinkedIn account at the slot. It runs the same checks as the Schedule button in the dashboard: a connected LinkedIn account, a plan that allows scheduling, a slot at least a couple of minutes ahead. Calling it on a scheduled post reschedules it. Requires posts:write.
| Field | Type | Description |
|---|---|---|
| scheduled_at | string | Required. ISO 8601 with an offset (2026-10-14T08:30:00+02:00), or a local YYYY-MM-DDTHH:mm read in timezone. A bare date is refused. |
| timezone | string | IANA zone for a local scheduled_at. Defaults to Settings, Publishing. |
| randomize | boolean | Spread the publish time within ±4 minutes so it never looks automated. Defaults to your publishing setting; false for the exact minute. |
curl -X POST https://myfeedin.co/api/v1/drafts/POST_ID/schedule \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"scheduled_at":"2026-10-14T08:30","timezone":"Europe/Paris"}'Errors carry a code: plan_required or paid_plan_required and account_missing, account_disconnected or token_expired (403), invalid with a problems list (422), invalid_status (409).
Unschedule a post
Puts a scheduled post back to draft. Nothing goes out. Requires posts:write.
Campaigns
List campaigns
Lists your outreach campaigns with per-campaign enrollment counts and step counts. Requires campaigns:read.
{
"campaigns": [
{
"id": "…",
"name": "Solopreneurs Q3",
"status": "active",
"step_count": 3,
"enrollment_counts": { "total": 42, "active": 30, "replied": 4 }
}
]
}Create campaign
Creates a draft campaign, locked to your connected LinkedIn account. Requires campaigns:write and a connected account.
| Field | Type | Description |
|---|---|---|
| name | string | Campaign name. Defaults to Untitled campaign. |
| icon | string | Optional icon name. Defaults to Megaphone. |
curl -X POST https://myfeedin.co/api/v1/campaigns \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Solopreneurs Q3"}'Get campaign
One campaign with its ordered steps, warmup state, and enrollments (each joined to its person). Requires campaigns:read.
Add profiles by URL
Adds LinkedIn profiles to a campaign by URL. Each URL is resolved through your connected LinkedIn account, upserted into your CRM, then enrolled. Requires campaigns:write and the Growth plan.
| Field | Type | Description |
|---|---|---|
| linkedin_url | string | A single profile URL. |
| linkedin_urls | string[] | Up to 25 profile URLs. Use instead of linkedin_url for batches. |
curl -X POST https://myfeedin.co/api/v1/campaigns/CAMPAIGN_ID/profiles \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"linkedin_url":"https://www.linkedin.com/in/some-person"}'{
"enrolled": 1,
"created": 1,
"matched": 0,
"skipped": 0,
"skipped_active_elsewhere": 0
}Guides
Guide: add a profile to a campaign
The most common workflow: find a campaign, then push profiles into it by URL. First list your campaigns to get an id.
curl https://myfeedin.co/api/v1/campaigns \
-H "Authorization: Bearer mfi_live_your_key_here"Then add one or more profiles to it.
curl -X POST https://myfeedin.co/api/v1/campaigns/CAMPAIGN_ID/profiles \
-H "Authorization: Bearer mfi_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"linkedin_urls": [
"https://www.linkedin.com/in/person-a",
"https://www.linkedin.com/in/person-b"
]
}'A person already active in another campaign is skipped rather than enrolled twice, and appears in skipped_active_elsewhere. A URL that cannot be resolved (private or out of network) is reported in an errors array without failing the rest of the batch.
Guide: manage a feed
Keep a feed clean from a script: list your feeds to get an id, list the people in it, then remove the ones that no longer belong.
curl https://myfeedin.co/api/v1/feeds \
-H "Authorization: Bearer mfi_live_your_key_here"curl https://myfeedin.co/api/v1/feeds/FEED_ID/people \
-H "Authorization: Bearer mfi_live_your_key_here"curl -X DELETE https://myfeedin.co/api/v1/feeds/FEED_ID/people/PERSON_ID \
-H "Authorization: Bearer mfi_live_your_key_here"Removing a person only detaches them from that feed; their CRM record and their other feed memberships are untouched. To rebuild the list, add people again from the app, the Chrome extension, or the add-people endpoint if you are on the Growth plan.