Documentation Index
Fetch the complete documentation index at: https://docs.tamradar.com/llms.txt
Use this file to discover all available pages before exploring further.
👤 For humans
Give this to your agent and it will set up TAMradar monitoring for you — create radars, check balance, retrieve findings.
Give this link to your agent: https://tamradar.readme.io/reference/tamradar-agentmd.md
You set up TAMradar monitoring for a user. Read this file top to bottom before starting.
Base URL: https://api.tamradar.com
Auth header: x-api-key: {TAMRADAR_API_KEY} — required on every request. No Bearer prefix.
Authentication
Check the environment for TAMRADAR_API_KEY. If the variable is unset or empty, ask the user:
“I need your TAMradar API key to proceed. You can find it in your account settings at tamradar.com.”
Never guess or construct the key. Do not proceed without it.
Canonical request format — use this exact pattern for every call:
curl -s -X POST "https://api.tamradar.com/v1/radars/companies" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "openai.com", "radar_type": "company_job_openings" }'
x-api-key is the auth header. Content-Type: application/json is required on all POST requests.
How this works — overview
1. PRE-FLIGHT GET /v1/account → check balance, block if $0
2. CREATE POST /v1/radars/companies → company signals
POST /v1/radars/contacts → person signals
POST /v1/radars/industry → industry-wide signals
POST /v1/radars/bulk → >10 targets: async queue (max 1000 items per request)
3. RETRIEVE GET /v1/updates → poll for findings (always available)
[webhook] → push delivery (faster, if webhook_url set)
4. MANAGE DELETE /v1/radars/:id → stop monitoring a target
Detailed breakdown follows.
| Input | Required | Notes |
|---|
TAMRADAR_API_KEY | MUST have | UUID format — check env first, ask if unset |
| Target list | MUST have | Companies (domain), people (name + LinkedIn URL + company domain), or industry keywords |
webhook_url | Optional | Omit for poll-only. Add it for real-time push delivery. |
Webhook URL — default to polling
webhook_url is optional. Default to polling via GET /v1/updates unless the user explicitly provides a webhook URL.
If no webhook URL provided: omit webhook_url entirely — the radar will be poll-only. Retrieve findings via GET /v1/updates. No need to ask.
If user provides a real webhook URL (Make.com, Clay, Zapier, n8n): include it. Real-time delivery will work automatically.
⚠️ Once set, webhook_url cannot be changed without deactivating and recreating the radar. Avoid ephemeral test URLs like webhook.site for production radars.
Phase 1: Pre-flight — ALWAYS run first
Sample response:
{
"status": "success",
"code": 200,
"data": {
"balance_remaining_usd": 42.50,
"account": {
"active_radars": 12,
"inactive_radars": 2,
"failed_radars": 0,
"total_radars": 14,
"total_updates_found": 3847
},
"usage": {
"month": "2026-05",
"radars_activated": 3,
"radars_deactivated": 0,
"radars_failed": 0,
"balance_used_usd": 7.50,
"updates_found": 142
}
}
}
Balance gate — enforce strictly:
- IF
data.balance_remaining_usd == 0 → STOP. Tell user: “Your TAMradar balance is $0. Add funds at tamradar.com before I can create any radars.”
- IF
data.balance_remaining_usd < 5 → WARN. Tell user the balance, ask if they want to continue.
- IF
data.balance_remaining_usd >= 5 → Proceed. Report balance to user.
Recommend: balance monitoring cron (to user only — do not auto-install)
Tell the user:
“To avoid radars being deactivated unexpectedly, I recommend setting up a daily job that calls GET /v1/account and alerts you if balance_remaining_usd drops below a threshold (e.g. 10).TAMradarauto−deactivatesradarswhenbalancehits0 and sends a radar_failure webhook — but proactive monitoring is better than reacting after the fact.”
Only suggest this. Do not install cron jobs, crontab entries, or scheduled tasks without explicit instruction.
Phase 2: Create radars
Idempotency — check local store first
Before every POST, look up (domain, radar_type) in ./tamradar-radars.jsonl (your local mapping store). If a matching entry exists, skip the POST and use the stored radar_id. This avoids wasteful requests and eliminates a whole class of 409 errors.
Singles vs bulk
- ≤10 targets → create one by one (single POSTs). Simple, easy to debug.
- >10 targets → use
POST /v1/radars/bulk (async queue, max 1000 items per request).
Single radar creation
custom_fields recommendation: always pass your internal IDs here. They echo back in every finding, making it easy to route data back to your CRM, database, or workflow.
"custom_fields": {
"crm_account_id": "0011U00000TFV7MQAX",
"owner": "alice@yourcompany.com",
"label": "enterprise-tier"
}
Company radar
curl -s -X POST "https://api.tamradar.com/v1/radars/companies" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "openai.com",
"radar_type": "company_job_openings",
"webhook_url": "https://hook.make.com/your-scenario-url",
"departments": ["Engineering", "Research"],
"seniorities": ["Senior", "Director", "Vice President", "CXO"],
"custom_fields": { "crm_account_id": "0011U00000TFV7MQAX" }
}'
curl -s -X POST "https://api.tamradar.com/v1/radars/contacts" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "contact_job_changes",
"domain": "openai.com",
"profile_url": "https://www.linkedin.com/in/samaltman",
"full_name": "Sam Altman",
"webhook_url": "https://hook.make.com/your-scenario-url",
"custom_fields": { "crm_contact_id": "003Dn00000AHtLQIA1" }
}'
Industry radar
curl -s -X POST "https://api.tamradar.com/v1/radars/industry" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "industry_funding_rounds",
"webhook_url": "https://hook.make.com/your-scenario-url",
"custom_fields": { "label": "ai-funding-watch" }
}'
⚠️ CRITICAL: persist the mapping after every 201
Store immediately to ./tamradar-radars.jsonl (append one JSON object per line):
{ "input_domain": "openai.com", "input_radar_type": "company_job_openings", "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b", "created_at": "2026-05-01T09:05:00Z" }
This file IS your monitoring state. Without it you cannot poll by specific radar, deactivate targets, or check idempotency before creating.
Bulk creation — for >10 targets
Send up to 1000 radars per request. webhook_url is required at the envelope level and applies to all items in the submission.
curl -s -X POST "https://api.tamradar.com/v1/radars/bulk" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://hook.make.com/your-scenario-url",
"radars": [
{ "domain": "openai.com", "radar_type": "company_job_openings", "custom_fields": { "crm_account_id": "001" } },
{ "domain": "anthropic.com", "radar_type": "company_new_hires", "custom_fields": { "crm_account_id": "002" } }
]
}'
Bulk submission response — HTTP 202 accepted:
- Response returns
bulk_id and submission summary only.
- Item-level outcomes are asynchronous and arrive via:
- webhook events (
radar_created / radar_failure / bulk_completed)
GET /v1/radars/bulk/{bulk_id} polling
Phase 3: Error handling
MUST handle every error before reporting success.
| Code | Meaning | Action |
|---|
401 | Invalid API key | STOP. Ask user to verify key. |
402 | Insufficient balance | STOP. Tell user to add funds, then retry. |
409 | Radar already exists | NOT an error — it’s already running. Extract radar_id from errors[0].reason. Store it. Move on. |
400 | Validation error | Show errors[].field + errors[].reason verbatim. Ask user to correct. |
429 | Rate limited | Wait, then retry. NEVER retry more than 3 times. |
5xx | Server error | Retry once. If still failing, report error_id to user and STOP. |
Phase 4: Retrieve findings (polling)
GET /v1/updates returns the same data as webhooks. The response uses updates[] — not data[].
Note: findings are retained for 60 days. Data older than 60 days is purged.
Note: radar_failure events are excluded by default. Default update_type is radar_finding only.
Sample response:
{
"status": "success",
"code": 200,
"updates": [
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_finding",
"discovered_at": "2026-05-01T08:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "openai.com",
"custom_fields": { "crm_account_id": "001" }
},
"content": { }
}
],
"has_more": false,
"next_cursor": null
}
Fetch findings for specific radars (max 10 radar_ids per call)
GET /v1/updates?radar_id={id1},{id2}&limit=50
If you have more than 10 radars, split into multiple calls of up to 10 IDs each.
Fetch by signal type
GET /v1/updates?radar_type=company_job_openings,company_new_hires&limit=50
Incremental — only new since last check
GET /v1/updates?since={last_discovered_at}&limit=100
ALWAYS store discovered_at from the last update you processed. Pass it as since on the next call.
IF has_more: true → fetch next page:
GET /v1/updates?cursor={next_cursor}
Repeat until has_more: false.
To include radar failures in polling
GET /v1/updates?update_type=radar_finding,radar_failure
Presenting findings to the user
Group by data.radar_type. Always include discovered_at. Example:
New job at openai.com — Senior Research Engineer, Alignment
Detected: 2026-04-29 · View posting
Phase 5: Deactivate radars
curl -s -X DELETE "https://api.tamradar.com/v1/radars/{radar_id}" \
-H "x-api-key: $TAMRADAR_API_KEY"
- Irreversible. Must create a new radar to resume monitoring.
- No further charges after deactivation.
IF user says “stop monitoring openai.com for job openings” → look up radar_id from ./tamradar-radars.jsonl and DELETE it.
Phase 6: Retrieving findings
Default: use polling. GET /v1/updates always works regardless of webhook_url. Findings are available immediately after creation and retained for 60 days.
If the user later wants real-time push delivery:
“Your radars are active and I can fetch findings anytime you ask. For real-time delivery into Slack, your CRM, or an automation, connect a tool like Make.com or Clay — it generates a webhook URL you can give me and I’ll update the setup.”
Summary template — ALWAYS present when done
TAMradar setup complete.
Balance: $42.50 remaining
Radars active:
company_job_openings · openai.com · c70813b3-7e87-4ca7-90fd-8c64574d911b
contact_job_changes · Sam Altman · a4f28c91-3b12-4e70-9d55-7e3dda58f100 (already existed — skipped POST)
industry_funding_rounds · — · f9d01c55-82ba-4f3e-b461-2a9e7c11d830
Failures:
None
Mapping saved to:
./tamradar-radars.jsonl
To fetch findings now:
GET /v1/updates?radar_id=c70813b3-...,a4f28c91-...&limit=50
(max 10 IDs per call — split into multiple requests if needed)
Findings retained for 60 days.
Webhook: Make.com scenario (https://hook.make.com/...) ✓
— OR —
Polling only. Ask me anytime to check for new findings.
Radar type reference
Company — POST /v1/radars/companies
Required: domain, radar_type — webhook_url optional (omit for poll-only)
radar_type | What it tracks | Filterable? |
|---|
company_job_openings | New job postings | Yes |
company_new_hires | People joining the company | Yes |
company_promotions | Internal promotions | Yes |
company_reviews | Glassdoor/employer reviews | No |
company_mentions | Web mentions of the company | No |
company_social_posts | Company LinkedIn/social posts | No |
company_social_posts_cxo | Executive social posts | Yes |
company_social_engagements | Engagements on company posts | No |
Domain normalization
The API accepts any format — bare domain, URL, or with www/path/port. All are normalized automatically:
openai.com ✅
https://www.openai.com/about ✅ → normalized to openai.com
www.openai.com ✅ → normalized to openai.com
Best practice: pass the bare domain (openai.com).
Filters for filterable types
departments[] — OR within the array. Omit to match any department. Do not send an empty array (400 error).
seniorities[] — OR within the array. Omit to match any seniority. Do not send an empty array (400 error).
When both are provided, findings must match the department filter AND the seniority filter.
Valid departments:
Accounting, Administrative, Business Development, Consulting, Customer Success, Design, Education, Engineering, Finance, Human Resources, Information Technology, Legal, Manufacturing, Marketing, Media and Communication, Operations, Product Management, Project Management, Purchasing, Quality Assurance, Real Estate, Research, Sales, Support
Valid seniorities:
Owner, CXO, Vice President, Director, Manager, Senior, Entry, Training, Partner
NEVER invent department or seniority values. Use ONLY the values listed above or you will get a 400 error.
job_titles — boolean expression string (optional, overrides departments + seniorities)
When job_titles is set, it replaces departments and seniorities — you cannot combine them.
Supported operators: OR, AND, NOT. Multi-word terms must be double-quoted.
"CEO OR CTO"
"VP Engineering OR VP Sales"
"(CEO OR CTO) AND NOT Manager"
"\"Head of Engineering\" OR \"VP of Engineering\""
Contact — POST /v1/radars/contacts
Required: radar_type + type-specific identifiers — webhook_url optional (omit for poll-only):
radar_type | Additional required fields |
|---|
contact_job_changes | domain + at least one of: email, profile_url, full_name |
contact_social_engagements | profile_url (must contain linkedin.com/in/, x.com/, or twitter.com/) |
contact_social_posts | profile_url (same URL formats) |
Profile URL normalization
Pass the full URL. The API normalizes automatically:
https://www.linkedin.com/in/samaltman ✅ (preferred)
https://linkedin.com/in/samaltman ✅ → normalized to https://www.linkedin.com/in/samaltman
linkedin.com/in/samaltman ✅ → https:// added automatically
- Trailing slash stripped automatically
Uniqueness for contacts is keyed on profile_url + radar type — different radar types for the same person ARE allowed.
Industry — POST /v1/radars/industry
Required: radar_type. No domain field — webhook_url optional (omit for poll-only).
radar_type | Additional required fields |
|---|
industry_funding_rounds | None |
industry_new_companies | None |
industry_mentions | keyword (min 3 chars) |
industry_job_openings | keyword (min 3 chars) + optional countries[] (max 5) |
Key fields reference
| Field | Found in | Purpose |
|-------|----------|---------||
| radar_id | Creation response → data.radar_id | MUST store with input. Used for polling, deactivation. |
| update_id | Every finding payload | Deduplicate on this. NEVER process the same update_id twice. |
| discovered_at | Every finding payload | Store latest value. Use as since param for incremental polling. |
| custom_fields | Request body → echoed in every finding | Your internal IDs. Route findings back to your CRM using these. |
| data.radar_type | Every finding payload | Route your processing logic on this. |
| content | Every finding payload | The actual finding. Shape varies by radar_type. |
Hard limits
webhook_url is optional — omit for poll-only radars; include only when real-time push delivery is needed
- Rate limit: 100 requests / 60 seconds — need more? contact support@tamradar.com
- Bulk max: 100 radars per request
- Polling
radar_id filter: max 10 IDs per call — split into multiple requests if you have more
- Polling data retention: 60 days — older data is purged
- One radar per
domain + radar_type per account — duplicates get 409
- Contact uniqueness: per
profile_url + radar_type — different contact radar types for the same person ARE allowed
- Persistence: save mappings to
./tamradar-radars.jsonl by default