Start here
- In the dashboard, open Settings → API keys and create a key. It’s shown once: copy it somewhere safe.
- Send it with every request, as
Authorization: Bearer <key>. - Check it works:
GET /meanswers with your workspace.
Base URL: https://linkpls.app/api/v1. Requests and responses are JSON. The API comes with the Pro and Scale subscriptions, at no extra cost: there’s no separate API plan.
curl https://linkpls.app/api/v1/me \
-H "Authorization: Bearer lp_live_…"Keys and authentication
- A key starts with
lp_live_. We store only a hash of it, so a lost key can’t be shown again: revoke it and make a new one. - A key can do everything the dashboard can do to automations, including sending DMs to real people. Keep it on your server or in your automation tool, never in a web page or an app.
- Revoking a key in Settings stops it at once. Each key shows when it was last used.
- A key sees only your workspace: your accounts, automations and leads.
Errors
An error answers with an HTTP status and a body like this. issues comes with validation errors and names each field that’s wrong.
{
"error": {
"code": "validation_error",
"message": "The request has 1 problem.",
"issues": [{ "path": "dmMessage", "message": "String must contain at least 1 character(s)" }]
}
}| Status | code | Meaning |
|---|---|---|
| 400 | validation_error | Something in the request isn’t allowed. See issues. |
| 401 | unauthorized | No key, a malformed key, or a revoked one. |
| 402 | plan_limit | Your plan’s automation limit is reached. |
| 403 | plan_required | Your plan doesn’t include the API. |
| 404 | not_found | No such automation, account or endpoint in your workspace. |
| 429 | rate_limited | Too many requests. Wait for the seconds in Retry-After. |
| 400 | bad_request | The body isn’t valid JSON, or there’s no Instagram account to use. |
| 500 | internal_error | Something went wrong on our side. It’s logged; try again in a moment. |
Rate limits
Up to 120 requests a minute per key, counted over the last 60 seconds. Past that you get a 429 with a Retry-After header. DMs themselves are paced by linkpls whatever the API does, so creating automations quickly never makes DMs go out faster.
Endpoints
Lists answer { "data": [...] }; the ones that page also give nextCursor. Send it back as ?cursor= for the next page, until it’s null.
GET /me
Your workspace and plan, and which key you’re using. Handy to check a key.
GET /accounts
Your connected Instagram accounts, newest first. The first one is used when you don’t name an account.
GET /posts
Your posts and reels, newest first, to find a post’s id for an automation on one post. Query: q searches captions, limit (up to 50, default 20), cursor, accountId.
GET /automations
All your automations with their numbers. Query: status (live, paused or draft), accountId.
POST /automations
Create an automation. Only keywords and dmMessage are required; accountId defaults to your newest account and name to one like “GUIDE · any post”. It goes live at once, like pressing Go live, unless you send "isDraft": true. Answers 201 with the automation.
GET /automations/{id}
One automation, with its numbers.
PATCH /automations/{id}
Change any fields; the rest stay as they are. The result is checked as a whole, so for example switching mediaScope to SPECIFIC needs a mediaId.
POST /automations/{id}/activate
Go live: publishes a draft, or switches a paused automation back on.
POST /automations/{id}/pause
Stop answering. DMs already waiting to go out are dropped too, and aren’t sent if you switch it back on.
POST /automations/{id}/duplicate
A copy, as a paused draft. Answers 201.
DELETE /automations/{id}
Delete it, with its keywords and numbers. Your posts stay on Instagram. Answers 204.
GET /leads
People who gave an email or phone number in a DM, newest first. Query: q (username, email or phone), limit (up to 200, default 50), cursor, accountId.
The automation object
A GET answers with the same field names that POST and PATCH take, so you can change one and send it back. It also carries id, status (live, paused or draft), createdAt, updatedAt and stats: triggered, sent, failed, queued, skipped, linkClicks, greetingsSent and greetingsTapped.
| Field | What it is |
|---|---|
name | Only you see it. |
trigger | COMMENT (default), STORY_REPLY or SHARE. |
keywords | The words that start it. Stored in lower case. |
matchType | CONTAINS (default), EXACT, or ANY to answer every comment. |
fuzzy | Also match typos and similar words. |
mediaScope, mediaId | ALL (default: every post), SPECIFIC with a post’s mediaId, or NEXT_POST. |
dmMessage | The DM. Up to 1,000 bytes; {first_name} becomes their name. |
ctaButtons | Up to 3 link buttons: [{ "title": "Get it", "url": "https://…" }]. Titles up to 20 characters. |
publicReplies | Up to 10 replies posted under their comment, one picked at random each time. |
messageVariants | Up to 10 other versions of the DM. Each DM goes out as the message or one of these, picked at random. |
openerEnabled, openerMessage, openerButtonLabel | A greeting sent first, with a button; the DM follows when they tap it. |
followGate, followGateMessage, followRetryMessage, followRecheckLabel, followFallback | Ask them to follow before the link. followFallback: DELIVER or HOLD when Instagram can’t confirm the follow. |
leadFields, leadPrompt | Ask for EMAIL and/or PHONE before the link; answers go to Leads. |
backfillEnabled, backfillWindowHours | Also answer comments from the last 1 to 168 hours, once, when it goes live. |
alsoOnShare | Also answer people who share the post to their DMs. |
isDraft, isActive | Draft, live or paused. The activate and pause endpoints set these for you. |
Example: a keyword on your newest reel
Find the reel, then create an automation for it as a draft, check it in the dashboard, and switch it on.
# 1. your newest post or reel
curl "https://linkpls.app/api/v1/posts?limit=1" -H "Authorization: Bearer $LINKPLS_KEY"
# 2. an automation for it, as a draft
curl -X POST https://linkpls.app/api/v1/automations \
-H "Authorization: Bearer $LINKPLS_KEY" \
-H "Content-Type: application/json" \
-d '{
"keywords": ["guide"],
"mediaScope": "SPECIFIC",
"mediaId": "<the post id>",
"dmMessage": "Here it is, {first_name}! 🎉",
"ctaButtons": [{ "title": "Get the guide", "url": "https://example.com/guide" }],
"publicReplies": ["Sent! Check your DMs 📬"],
"isDraft": true
}'
# 3. switch it on
curl -X POST https://linkpls.app/api/v1/automations/<id>/activate -H "Authorization: Bearer $LINKPLS_KEY"Test it with a comment from a second account before you rely on it. Questions? See the help center.