mySDRDocs

API reference

Get Company Tech Stack

Send domains (company websites) and/or linkedin_urls (LinkedIn company pages), up to 25 together. Each gets its own entry in results, in the order you sent them (domains first). A technology appears when the company’s job posts mention it: first_seen_on and last_seen_on are the first and the last job post that did, jobs_mentioning how many, and confidence how sure the match is. A company that rarely hires has no tech stack here.

POSThttps://mysdr.app/api/v1/company/tech-stack
View Markdown

By default you get the technologies seen in the last 12 months with medium or high confidence, the most certain and most mentioned first, at most 50 per company; seen_within_months, min_confidence and limit change that, and left_out says what the defaults hid. To answer "does this company use X?", send technologies: asked then says yes or no per technology, with the evidence. A name the catalog does not know is 400 unknown_technology with the closest names in suggestions, before anything is charged. For one kind of tool, send categories (e.g. CRM, Marketing Automation, ERP).

Some names are also common words or job titles in job posts ("streamline", "team leader"). A word that is almost never the product is left out unless you ask for it by name (left_out.common_words counts them); a real product with such a name is shown with ambiguous: true, after the rest. Check its evidence before you rely on it.

A company costs the same whether your filters keep fifty technologies or none: the lookup was made. Every call is charged, also a repeat of the same company, so to answer several questions about one company, ask once without filters and answer from that. Not found, invalid, capped or failed entries cost nothing, and the same company in one call (also its domain and its LinkedIn page) is looked up and charged once. A domain finds a company more often than a LinkedIn page.

Authentication

Send Authorization: Bearer gtk_…. Create the key under Connect assistant → API keys. This endpoint answers for every key with API access: it is not behind the Signals API or the Company API. A missing, invalid or revoked key is 401. Every response includes mySDR-Api-Version, mySDR-Request-Id and Cache-Control: no-store, and a response to an accepted key also X-RateLimit-*; every error has type, message, param and request_id.

Body parameters

  • domains string[] — Company websites or domains, e.g. acme.com (a URL or an email address works too).
  • linkedin_urls string[] — LinkedIn company pages, e.g. https://www.linkedin.com/company/acme-analytics.
  • technologies string[] — Only these technologies, by slug or name, up to 20: "does this company use HubSpot?". asked answers per technology. A name the catalog does not know is a 400, free. Leave it out for the whole stack.
  • categories string[] — Only technologies of these kinds, up to 20, e.g. CRM, ERP, Marketing Automation or a whole area such as Sales or HR. Every word must be in the category or the technology’s name (CRM also finds HubSpot CRM and HubSpot). With technologies too, a technology must match both.
  • min_confidence string — The weakest evidence to include. Defaults to medium. One of: low, medium, high.
  • seen_within_months integer — Only technologies a job post mentioned in the last this many months, 1 to 120. Defaults to 12.
  • limit integer — Technologies per company, 1 to 200. Defaults to 50.
  • max_credits integer — Stop before the call costs more than this. A company is looked up only while it still fits; one we do not know costs nothing and leaves the room to the next. Entries left over are capped. Below the price of one company the call is 402 max_credits_exceeded.

Response

  • results object[] — One entry per company you sent, in the same order.
  • results[].input string — The domain or URL as you sent it.
  • results[].match_type string — domain or linkedin_url. One of: domain, linkedin_url.
  • results[].status string — matched, not_found (no job posts of this company in our data, or a LinkedIn page we could not match), invalid (not a company domain or LinkedIn company page; message says what to send instead), capped (not looked up: max_credits had no room left) or error (retry later). One of: matched, not_found, invalid, capped, error.
  • results[].company object — name, domain and linkedin_url, as far as we know them.
  • results[].technologies object[] — slug, name, category, parent_category, confidence (low, medium or high: how sure the match is), first_seen_on and last_seen_on (the first and the last job post that mentioned it) and jobs_mentioning (in how many job posts), plus jobs_last_30_days, rank_in_category (1 = the most mentioned tool of its kind at this company) and ambiguous (true when the name is also a common word or job title in job posts).
  • results[].technologies_total integer — Every technology we know for this company, before your filters (the data returns its 500 most certain).
  • results[].technologies_total_capped boolean — Only when true: the data returned its 500 most certain technologies and knows more, with less evidence.
  • results[].asked object[] — Only with technologies: one entry per technology you asked for, technology as you sent it and found true or false; when found, slug, name, confidence, last_seen_on, jobs_mentioning, meets_filters: false when it is there but older or weaker than your filters, and ambiguous when its name is also a common word.
  • results[].technologies_matching integer — How many passed your filters, before limit.
  • results[].left_out object — Only when something was hidden: lower_confidence and older (hidden by min_confidence and seen_within_months) and common_words (common words the data reads as a product, hidden unless you ask for them by name), how many.
  • results[].observed_at string — When this tech stack was fetched (ISO 8601); kept up to 30 days.
  • results[].credits_charged integer — 3 on the first entry of a company we know, otherwise 0.
  • results[].message string — Why an entry has no technologies, and what to send instead.
  • credits_used integer — Credits this call used.
  • truncated_by string — Only when max_credits left entries capped: max_credits. One of: max_credits.
  • sources string[] — Always ["job_postings"].

Credits

3 credits per company we know; a company we do not know costs nothing. Errors and empty lookups are free. A call that spent credits puts that number in credits_used, in X-Credits-Used and in X-Credits-Charged. When the wallet can be read, the response also sends X-Credits-Remaining.

Errors

  • 400 invalid_request — The body is not valid JSON, a field is unknown or has the wrong type, or a value is not allowed.
  • 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.
  • 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.
  • 429 daily_limit_reached — The request could take your organization over its daily credit limit. The limit resets at midnight (Europe/Brussels).
  • 402 insufficient_credits — The credit balance is lower than this request could cost. Nothing was charged. Add credits, or send a smaller request. The error includes code, fix and a link to the credits docs.
  • 502 upstream_error — The data source did not answer. Retry later.
  • 503 service_unavailable — The API is temporarily unavailable. Retry later.
  • 500 internal_error — Something went wrong on our side.
  • 402 max_credits_exceeded — max_credits is lower than the credits of the first row, so nothing could be returned. Nothing was charged; raise max_credits or ask for fewer rows.

Example

cURL

curl -X POST "https://mysdr.app/api/v1/company/tech-stack" \
  -H "Authorization: Bearer $MYSDR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domains": [
      "acme-analytics.example"
    ],
    "categories": [
      "CRM",
      "Marketing Automation"
    ]
  }'

JavaScript

const response = await fetch('https://mysdr.app/api/v1/company/tech-stack', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MYSDR_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "domains": [
      "acme-analytics.example"
    ],
    "categories": [
      "CRM",
      "Marketing Automation"
    ]
  })
});
const body = await response.json();

Python

import json
import os
import urllib.request

payload = {
  "domains": [
    "acme-analytics.example"
  ],
  "categories": [
    "CRM",
    "Marketing Automation"
  ]
}

request = urllib.request.Request(
    "https://mysdr.app/api/v1/company/tech-stack",
    headers={
    "Authorization": f"Bearer {os.environ['MYSDR_API_KEY']}",
    "Content-Type": "application/json"
    },
    data=json.dumps(payload).encode(),
    method="POST",
)
with urllib.request.urlopen(request) as response:
    body = json.load(response)

Example response

{
  "results": [
    {
      "input": "acme-analytics.example",
      "match_type": "domain",
      "status": "matched",
      "company": {
        "name": "Acme Analytics",
        "domain": "acme-analytics.example",
        "linkedin_url": null
      },
      "technologies": [
        {
          "slug": "hubspot",
          "name": "HubSpot",
          "category": "Marketing Automation Platforms",
          "parent_category": "Marketing",
          "confidence": "high",
          "first_seen_on": "2024-03-12",
          "last_seen_on": "2026-10-02",
          "jobs_mentioning": 14,
          "jobs_last_30_days": 2,
          "rank_in_category": 1,
          "ambiguous": false
        }
      ],
      "technologies_total": 61,
      "technologies_matching": 1,
      "observed_at": "2026-10-25T09:12:04.000Z",
      "credits_charged": 3
    }
  ],
  "credits_used": 3,
  "sources": [
    "job_postings"
  ]
}