API reference
Create a monitor
A standing keyword monitor. Creating it is free. Each later run charges per new mention it delivers. schedule hourly is 400. A keyword is a string, or an object with keyword and the settings below. A keyword of more than one word is searched as that phrase. The search only uses the keywords. The settings are checked on the text of each post it found, before the relevance check, so they can only drop posts. A post becomes a mention when its text has an active keyword and it passes that keyword's settings. A post whose text has no active keyword is dropped, because LinkedIn also matches names. The word lists match whole words, in any case and with or without accents.
Authentication
Send Authorization: Bearer gtk_…. Create the key under Connect assistant → API keys. A missing, invalid or revoked key is 401. A valid key while this API is not enabled for your organization answers 403 api_not_enabled, with what the key can do instead. Every response includes mySDR-Api-Version, mySDR-Request-Id and Cache-Control: no-store, and a response to an accepted key also X-RateLimit-* (so not a 401, a 405, an OPTIONS or a 404 for an unknown path).
Headers
Idempotency-Keystring— Optional. The same key and the same body replay the monitor that was created. The same key with a different body is409idempotency_conflict.
Body parameters
namestring· required — 1 to 120 characters.keywordsarray· required — 1 to 10 keywords: strings, or objects with the fields below.keywords[].keywordstring· required — The word or phrase, 2 to 100 characters.keywords[].contextstring | null— Up to 200 characters for the relevance check, such as what the keyword means for you.keywords[].tagstring | null— A label for the keyword. One of:own_brand,competitor,industry_term.keywords[].exact_matchboolean— Defaults to true: the keyword must stand on its own. A domain (acme.com), handle (@acme), hashtag (#acme, or#AcmeAnalyticsfor Acme Analytics) or possessive (acme's) counts. A plural (acmes) does not. A space and a hyphen between words count the same.false: the keyword may also sit inside a longer word (acmes,support.acme.io).keywords[].case_sensitiveboolean— Defaults to false: any case, with or without accents.true: only as written (RAG, notrag). The word lists always ignore case.keywords[].also_include_anystring[]— Up to 10 words. The post must also use at least one of them.keywords[].also_include_allstring[]— Up to 10 words. The post must also use all of them.keywords[].exclude_wordsstring[]— Up to 50 words. A post that uses one of them is dropped.keywords[].exclude_wildcardsstring[]— Up to 50 patterns. A post with a word that matches one is dropped. A pattern starts at the start of a word, and*stands for any characters within that word:beta.*dropsbeta.2andbeta.acme.io.keywords[].exclude_authorsstring[]— Up to 100 LinkedIn profile URLs. Posts by these people are dropped.keywords[].pausedboolean— Defaults to false. A paused keyword is not searched and counts for no post.schedulestring— Defaults todaily.hourlyis not available yet (400). One of:daily,hourly.min_relevancestring— Defaults tomedium. One of:high,medium,low.tagsstring[]— Tags to keep, such asown_brand_mention,competitor_mention,buy_intent.webhook_endpoint_idnull— A monitor sends no webhooks: an id is400. Read new mentions with GET /mentions?monitor_id=… instead (free).max_credits_per_dayinteger | null— Daily credit cap for the runs, at least the price of one mention: a lower cap is400. A run also stops at the organization’s daily credit limit.
Response
idstring· required — Monitor id,mon_….objectstring· required — Alwaysmonitor. One of:monitor.namestring· required — The name you gave it.statusstring· required —active,paused, ordeletedafter DELETE. One of:active,paused,deleted.schedulestring· required —daily.hourlyis not available yet. One of:daily,hourly.keywordsobject[]· required — Each keyword with all its settings,exact_matchandcase_sensitiveincluded, andpaused.min_relevancestring· required —high,mediumorlow. One of:high,medium,low.max_credits_per_dayinteger | null— Daily cap, or null for none.credits_todayinteger· required — Credits this monitor spent today (Europe/Brussels calendar day).credits_chargedinteger· required — Credits charged so far.next_run_atstring | null· required — When the next run starts; null while the monitor is paused or deleted.last_runobject | null· required — What the last run did:at,mentions_delivered,credits_chargedandstopped_by.stopped_byis null when the run read everything it found, ormax_credits_per_day(the monitor’s cap),daily_limit(the organization’s daily limit),balance(the wallet), orpausedordeleted(the monitor was paused or deleted during the run, which stops the charging). A run that a cap or the balance stopped before it read anything shows 0 mentions with that reason. Null before the first run.deleted_atstring | null· required — When DELETE stopped the monitor.mentions_readable_untilstring | null· required — For a deleted monitor: until when it and its mentions stay readable (30 days after DELETE).
Credits
Creating a monitor is free. A run charges 1 credit per new mention delivered. A run that finds nothing is free.
Errors
- 400
invalid_request— A keyword is too short, orscheduleishourly. - 401
authentication_error— The Authorization header is missing, or the key is unknown or revoked. - 403
permission_error— API access is not part of your plan, or your account is suspended. - 403
api_not_enabled— The key is valid, but the Signals API or the Company API is not enabled for your organization yet. The error says what this key can do. - 429
rate_limit_error— More than 60 requests per minute, or more than 3 at the same time, on one key. Wait for the Retry-After header. - 500
internal_error— Something went wrong on our side.
Example
cURL
curl -X POST "https://mysdr.app/api/v1/monitors" \
-H "Authorization: Bearer $MYSDR_API_KEY" \
-H "Idempotency-Key: idem-1" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme mentions",
"keywords": [
{
"keyword": "Acme Analytics",
"tag": "own_brand",
"exact_match": true,
"exclude_words": [
"vacancy"
],
"exclude_authors": [
"https://www.linkedin.com/in/jane-example"
]
}
],
"max_credits_per_day": 50
}'
JavaScript
const response = await fetch('https://mysdr.app/api/v1/monitors', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MYSDR_API_KEY}`,
'Idempotency-Key': 'idem-1',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"name": "Acme mentions",
"keywords": [
{
"keyword": "Acme Analytics",
"tag": "own_brand",
"exact_match": true,
"exclude_words": [
"vacancy"
],
"exclude_authors": [
"https://www.linkedin.com/in/jane-example"
]
}
],
"max_credits_per_day": 50
})
});
const body = await response.json();
Python
import json
import os
import urllib.request
payload = {
"name": "Acme mentions",
"keywords": [
{
"keyword": "Acme Analytics",
"tag": "own_brand",
"exact_match": True,
"exclude_words": [
"vacancy"
],
"exclude_authors": [
"https://www.linkedin.com/in/jane-example"
]
}
],
"max_credits_per_day": 50
}
request = urllib.request.Request(
"https://mysdr.app/api/v1/monitors",
headers={
"Authorization": f"Bearer {os.environ['MYSDR_API_KEY']}",
"Idempotency-Key": "idem-1",
"Content-Type": "application/json"
},
data=json.dumps(payload).encode(),
method="POST",
)
with urllib.request.urlopen(request) as response:
body = json.load(response)
Example response
{
"id": "mon_01J9Z6P8C8V0K3W6T2N8R5M1XF",
"object": "monitor",
"name": "Acme mentions",
"status": "active",
"schedule": "daily",
"credits_charged": 0
}