Skip to main content
This guide walks through a complete TAMradar integration from scratch. We’ll use a concrete scenario throughout: monitoring OpenAI, including job openings, executive moves, and AI funding activity across the industry. By the end you’ll have three active radars, a working webhook handler, and know how to poll for updates programmatically. Prerequisites
  • A TAMradar API key (UUID format, e.g. a1b2c3d4-...)
  • A publicly accessible HTTPS endpoint that accepts POST requests — use webhook.site for testing
  • Any HTTP client (curl, Axios, fetch, etc.)
Base URL: https://api.tamradar.com Auth header: x-api-key: YOUR_API_KEY (required on every request)

Step 1: Verify Your Account & Balance

Before creating anything, confirm your key works and you have enough balance.
curl https://api.tamradar.com/v1/account \
  -H "x-api-key: YOUR_API_KEY"
{
  "status": "success",
  "code": 200,
  "message": "Account summary retrieved",
  "data": {
    "balance_remaining_usd": 29.85,
    "account": {
      "total_radars": 12,
      "active_radars": 8,
      "inactive_radars": 4,
      "failed_radars": 0,
      "total_updates_found": 541
    },
    "usage": {
      "month": "2026-04",
      "balance_used_usd": 3.20,
      "radars_activated": 4,
      "radars_deactivated": 1,
      "radars_failed": 0,
      "updates_found": 63
    }
  },
  "timestamp": "2026-04-30T09:00:00Z"
}
balance_remaining_usd is what you have to spend. Each radar type has its own price. If you don’t have enough to cover a radar creation, you’ll get a 402 before anything is charged.

Step 2: Test Your Webhook Endpoint

Send a sample payload to your endpoint before committing balance. This is fire-and-forget. It won’t create a radar or charge anything.
curl -X POST https://api.tamradar.com/v1/webhooks/test \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://webhook.site/your-unique-id",
    "radar_type": "company_job_openings"
  }'
Check your endpoint. You should receive a sample radar_finding payload. If it arrives, your endpoint is reachable and returning 2xx. Proceed.
You can also test a failure payload: add "update_type": "radar_failure" to the body.

Step 3: Create a Company Radar (Job Openings at OpenAI)

Track new engineering job openings posted by OpenAI, filtered to Senior-level and above.
webhook_url is optional. Include it to receive real-time push notifications. Omit it to poll for updates via GET /v1/updates instead.
curl -X POST https://api.tamradar.com/v1/radars/companies \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "openai.com",
    "radar_type": "company_job_openings",
    "webhook_url": "https://webhook.site/your-unique-id",
    "departments": ["Engineering", "Research"],
    "seniorities": ["Senior", "Director", "Vice President", "CXO"],
    "custom_fields": {
      "account_id": "openai-001",
      "priority": "high"
    }
  }'
201 — Created:
{
  "status": "success",
  "code": 201,
  "message": "Radar created successfully",
  "data": {
    "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
    "radar_type": "company_job_openings",
    "domain": "openai.com",
    "webhook_url": "https://webhook.site/your-unique-id",
    "radar_status": "active",
    "created_at": "2026-04-30T09:05:00Z",
    "deactivated_at": null,
    "next_charge_at": "2026-05-30T09:05:00Z",
    "departments": ["Engineering", "Research"],
    "seniorities": ["Senior", "Director", "Vice President", "CXO"],
    "custom_fields": {
      "account_id": "openai-001",
      "priority": "high"
    }
  },
  "timestamp": "2026-04-30T09:05:00Z"
}
Save radar_id. You’ll use it to check status or deactivate the radar. next_charge_at is your next billing date, 30 days from creation.

What can go wrong

CodeReasonFix
402Insufficient balanceTop up your account, then retry
409Radar already exists for openai.com + company_job_openingsUse the radar_id in the error response to manage the existing radar
400Invalid domain, bad department value, invalid webhook_url (if provided)Check errors[].reason for the exact field and issue
401Missing or invalid API keyVerify x-api-key header is present and correct

Step 4: Create a Contact Radar (Track Sam Altman)

Monitor job changes for a specific person. Requires domain + at least one identifier (email, profile_url, or full_name).
curl -X POST https://api.tamradar.com/v1/radars/contacts \
  -H "x-api-key: YOUR_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://webhook.site/your-unique-id",
    "custom_fields": {
      "account_id": "openai-001",
      "contact_type": "executive"
    }
  }'
201 — Created:
{
  "status": "success",
  "code": 201,
  "message": "Radar created successfully",
  "data": {
    "radar_id": "a4f28c91-3b12-4e70-9d55-7e3dda58f100",
    "radar_type": "contact_job_changes",
    "domain": "openai.com",
    "webhook_url": "https://webhook.site/your-unique-id",
    "radar_status": "active",
    "created_at": "2026-04-30T09:08:00Z",
    "deactivated_at": null,
    "next_charge_at": "2026-05-30T09:08:00Z",
    "custom_fields": {
      "account_id": "openai-001",
      "contact_type": "executive"
    }
  },
  "timestamp": "2026-04-30T09:08:00Z"
}
Contact radar type requirements:
radar_typeRequired fields
contact_job_changesdomain + one of (email, profile_url, full_name)
contact_social_engagementsprofile_url (must be linkedin.com/in/, x.com/, or twitter.com/)
contact_social_postsprofile_url (same as above)

Step 5: Create an Industry Radar (AI Funding Rounds)

Track funding events across the AI industry, with no specific company or keyword needed.
curl -X POST https://api.tamradar.com/v1/radars/industry \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "radar_type": "industry_funding_rounds",
    "webhook_url": "https://webhook.site/your-unique-id",
    "custom_fields": {
      "segment": "ai"
    }
  }'
201 — Created:
{
  "status": "success",
  "code": 201,
  "message": "Radar created successfully",
  "data": {
    "radar_id": "f9d01c55-82ba-4f3e-b461-2a9e7c11d830",
    "radar_type": "industry_funding_rounds",
    "domain": null,
    "webhook_url": "https://webhook.site/your-unique-id",
    "radar_status": "active",
    "created_at": "2026-04-30T09:10:00Z",
    "deactivated_at": null,
    "next_charge_at": "2026-05-30T09:10:00Z",
    "custom_fields": {
      "segment": "ai"
    }
  },
  "timestamp": "2026-04-30T09:10:00Z"
}
domain is always null for industry radars. Industry radar type requirements:
radar_typeRequires keyword?
industry_funding_roundsNo
industry_new_companiesNo
industry_mentionsYes (min 3 chars)
industry_job_openingsYes (min 3 chars) + optional countries[] (max 5)

Step 6: Scale Up with Bulk Creation

Submit up to 1000 radars in one async bulk request. Useful when onboarding large account lists.
curl -X POST https://api.tamradar.com/v1/radars/bulk \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://webhook.site/your-unique-id",
    "radars": [
      {
        "domain": "openai.com",
        "radar_type": "company_new_hires",
        "departments": ["Engineering"],
        "seniorities": ["Senior", "Director"],
        "custom_fields": { "account_id": "openai-001" }
      },
      {
        "domain": "anthropic.com",
        "radar_type": "company_job_openings",
        "custom_fields": { "account_id": "anthropic-001" }
      },
      {
        "domain": "openai.com",
        "radar_type": "company_job_openings"
      }
    ]
  }'
webhook_url is required for async bulk submissions. Bulk processing runs in the background and emits item outcomes (radar_created / radar_failure) plus a final bulk_completed. 202 — Accepted (request queued for async processing):
{
  "status": "success",
  "code": 202,
  "message": "Bulk submission accepted — radars are being created asynchronously",
  "bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
  "summary": { "total": 3 },
  "timestamp": "2026-04-30T09:15:00Z"
}
Track progress with the returned bulk_id:
curl "https://api.tamradar.com/v1/radars/bulk/5f9da22e-4778-427c-b7e4-dab2a8d9d709" \
  -H "x-api-key: YOUR_API_KEY"
The status endpoint returns:
  • status: "processing" while there are in-flight items
  • status: "completed" when all items are terminal
  • summary counts (processing, created, failed)
  • radars[] with item_index so you can map outcomes back to input order
  • radars[].update_id so you can correlate each item with its radar_created / radar_failure event
For webhook idempotency/correlation in async bulk:
  • Use top-level update_id on bulk_completed to deduplicate the whole batch event
  • Use radars[].update_id inside bulk_completed to correlate each item-level event

Step 7: Handle Failure Webhooks

When a radar fails during setup, TAMradar sends a radar_failure webhook to your endpoint and refunds the charge. missing_prerequisites — The company has no trackable public data source for this radar type. Balance is refunded.
{
  "update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
  "update_type": "radar_failure",
  "record_id": null,
  "discovered_at": "2026-04-30T10:00:00Z",
  "completed_at": "2026-04-30T10:00:00Z",
  "data": {
    "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
    "radar_type": "company_job_openings",
    "domain": "openai.com",
    "next_charge_at": null,
    "custom_fields": { "account_id": "openai-001", "priority": "high" }
  },
  "status": "error",
  "code": 400,
  "message": "Radar creation failed because the domain does not have required data sources. Credits have been refunded.",
  "errors": [{ "field": "radar", "reason": "missing_prerequisites" }],
  "refund_amount_usd": 0.10,
  "timestamp": "2026-04-30T10:00:00Z",
  "error_id": "41991828-ed31-4bd9-98d2-44b2763f9f35"
}
insufficient_funds — Recurring billing failed; radar was deactivated. No refund (the radar ran; it just couldn’t renew).
{
  "update_type": "radar_failure",
  "data": { "radar_id": "...", "radar_type": "company_job_openings", "next_charge_at": null },
  "status": "error",
  "code": 402,
  "message": "Your radar was deactivated due to insufficient funds.",
  "errors": [{ "field": "radar", "reason": "insufficient_funds" }],
  "refund_amount_usd": 0
}
Failure reference — identified by code and message:
Failure typecodeRefund?What to do
Missing prerequisites400YesVerify the company has accessible public profiles
Setup failure400YesContact support
Insufficient funds402NoTop up balance; create a new radar to resume
Source inaccessible400YesTarget profile is private or has been removed

Step 8: Receive Webhook Findings

When a radar detects something, it POSTs to your webhook_url. Every payload shares the same base structure. Only content varies by radar type.

Company job opening at OpenAI

{
  "update_id": "efbf7c77-c1ae-4a0d-85b6-116674e9e0b1",
  "update_type": "radar_finding",
  "record_id": "b77af154-c369-4446-b70a-f328880c3a48",
  "discovered_at": "2026-04-30T14:30:00Z",
  "data": {
    "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
    "radar_type": "company_job_openings",
    "domain": "openai.com",
    "next_charge_at": "2026-05-30T09:05:00Z",
    "custom_fields": { "account_id": "openai-001", "priority": "high" }
  },
  "content": {
    "job_title": "Senior Research Engineer, Alignment",
    "job_url": "https://openai.com/careers/senior-research-engineer-alignment",
    "job_posted_at": "2026-04-29",
    "job_description": "We are looking for a Senior Research Engineer to work on alignment and safety...",
    "job_source": "Company Website",
    "country": "United States"
  }
}

New hire at OpenAI

{
  "update_id": "a1b2c3d4-0001-4000-b000-000000000001",
  "update_type": "radar_finding",
  "record_id": "d99bf154-1234-4446-b70a-abc123456789",
  "discovered_at": "2026-04-30T15:00:00Z",
  "data": {
    "radar_id": "...",
    "radar_type": "company_new_hires",
    "domain": "openai.com",
    "next_charge_at": "2026-05-30T09:05:00Z",
    "custom_fields": { "account_id": "openai-001" }
  },
  "content": {
    "full_name": "Jane Doe",
    "first_name": "Jane",
    "last_name": "Doe",
    "profile_url": "https://www.linkedin.com/in/janedoe/",
    "title": "Senior Software Engineer",
    "start_date": "2026-04-15",
    "country": "United States"
  }
}
Key fields for your handler:
  • update_id — use for idempotency. If you receive the same update_id twice, skip it.
  • custom_fields — your metadata, echoed back exactly as set on the radar.
  • discovered_at — use for ordering and incremental polling (see Step 9).
  • Return any 2xx status within 10 seconds. TAMradar will retry on failure (up to 3 retries: 1m, 5m, 15m).

Handling webhooks in your server

Acknowledge first, process after. TAMradar waits up to 10 seconds for a 2xx. If you do slow work (database writes, third-party calls) synchronously before responding, you risk a timeout and a retry.
// Node.js + Express
app.post('/webhooks/tamradar', express.json(), async (req, res) => {
  // Acknowledge immediately — processing happens after
  res.sendStatus(200);

  const { update_id, update_type, data, code, message } = req.body;

  // Deduplicate — TAMradar delivers at-least-once, so duplicates are possible
  const alreadySeen = await db.processedUpdates.exists(update_id);
  if (alreadySeen) return;
  await db.processedUpdates.insert(update_id);

  if (update_type === 'radar_failure') {
    // Alert your team — the radar needs attention
    await alertOps(`Radar ${data.radar_id} failed (${code}): ${message}`);
    return;
  }

  // Route by radar type — custom_fields carry your own identifiers
  switch (data.radar_type) {
    case 'company_job_openings':
      await notifySlack(`New role at ${data.domain}: ${content.job_title}${content.job_url}`);
      break;
    case 'company_new_hires':
      await updateCRM(data.custom_fields.account_id, { new_hire: content });
      break;
    case 'contact_job_changes':
      await triggerSalesSequence(data.custom_fields.contact_id, content);
      break;
    case 'industry_funding_rounds':
      await logFundingEvent(content);
      break;
  }
});
The pattern applies in any language. The key constraints are to respond 2xx before doing work and deduplicate on update_id.

Step 9: Poll for Updates (Alternative to Webhooks)

GET /v1/updates returns the same payloads that webhooks deliver. Use this to backfill missed events, audit history, or as your primary data retrieval method. Fetch all updates for your OpenAI radars since yesterday:
curl "https://api.tamradar.com/v1/updates?domain=openai.com&since=2026-04-29T00:00:00Z" \
  -H "x-api-key: YOUR_API_KEY"
Fetch a specific radar type, paginated:
curl "https://api.tamradar.com/v1/updates?radar_type=company_job_openings&limit=50" \
  -H "x-api-key: YOUR_API_KEY"
Response:
{
  "status": "success",
  "code": 200,
  "message": "Updates retrieved successfully",
  "updates": [ /* same shape as webhook payloads */ ],
  "has_more": true,
  "next_cursor": "eyJyIjoiMjAyNi0wNC0xM1QyMToyMjo1OVoiLCJpIjoiZWZiZjdjNzctYzFhZS00YTBkLTg1YjYtMTE2Njc0ZTllMGIxIn0=",
  "timestamp": "2026-04-30T10:00:00Z"
}
Paginate:
curl "https://api.tamradar.com/v1/updates?cursor=eyJyIjoiMjAyNi0wNC0x..." \
  -H "x-api-key: YOUR_API_KEY"
Keep fetching with next_cursor until has_more: false. Incremental polling loop:
1. Call GET /v1/updates?since={last_discovered_at}
2. Process updates in order
3. Store the highest discovered_at you've seen
4. While has_more: pass next_cursor to get next page
5. Sleep or repeat on schedule
radar_failure events are excluded by default. To include them: ?update_type=radar_finding,radar_failure Data retention: 60 days. Poll at least that often if you don’t use webhooks.

Step 10: Monitor Your Radars

List all active radars:
curl "https://api.tamradar.com/v1/radars" \
  -H "x-api-key: YOUR_API_KEY"
Get a specific radar:
curl "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
  -H "x-api-key: YOUR_API_KEY"
Key fields to watch: radar_status (active/inactive), next_charge_at (next billing date), deactivated_at.

Step 11: Deactivate a Radar

When you no longer need a radar, delete it to stop billing.
curl -X DELETE "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
  -H "x-api-key: YOUR_API_KEY"
{
  "status": "success",
  "code": 200,
  "message": "Radar deactivation initiated - service will continue until the end of the current billing cycle",
  "data": {
    "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
    "radar_status": "inactive",
    "deactivated_at": "2026-04-30T10:30:00Z",
    "next_charge_at": null
  },
  "timestamp": "2026-04-30T10:30:00Z"
}
Deactivation is irreversible. Create a new radar to resume monitoring. On monthly billing, the radar continues running until the end of the current billing cycle. On update-based billing, deactivation is immediate.

Before You Ship

  • Deduplicate on update_id — webhooks are at-least-once. Store processed IDs and skip duplicates.
  • Respond 2xx within 10 seconds — acknowledge the request before doing any slow work.
  • Handle radar_failure webhooks — alert your team when a radar fails so it doesn’t silently stop.
  • Monitor balance_remaining_usd — set up an alert when it drops below your comfort threshold. A zero balance deactivates radars without warning.
  • Store radar_id per account — you’ll need it to list, inspect, or deactivate a radar later.
  • Test your endpoint with /v1/webhooks/test before creating your first paid radar.
  • Use since for incremental polling — if you use /v1/updates, store the last discovered_at and pass it on the next call. Don’t re-fetch everything on every poll.

Quick Reference

MethodEndpointWhat it does
GET/v1/accountCheck balance and radar counts
POST/v1/webhooks/testSend a test payload to a URL
POST/v1/radars/companiesCreate a company radar
POST/v1/radars/contactsCreate a contact radar
POST/v1/radars/industryCreate an industry radar
POST/v1/radars/bulkQueue up to 1000 radars asynchronously
GET/v1/radars/bulk/:bulk_idPoll async bulk processing status
GET/v1/radarsList all radars
GET/v1/radars/:idGet a single radar
DELETE/v1/radars/:idDeactivate a radar
GET/v1/updatesPoll for radar findings
Rate limits: TAMradar uses separate write/read budgets plus a dedicated bulk budget.
HeaderDescription
X-RateLimit-LimitPer-minute budget for the matched bucket
X-RateLimit-RemainingRemaining tokens in that bucket
X-RateLimit-ResetSeconds until bucket reset (when available)
On 429, honor Retry-After before retrying. Error codes:
CodeMeaning
400Validation error — check errors[].field and errors[].reason
401Missing or invalid x-api-key
402Insufficient balance — top up and retry
409Radar already exists — use the radar_id in the error to manage it
429Rate limit exceeded — back off and retry
5xxServer error — retry with exponential backoff
Every error response includes error_id. Include it when contacting support.