# Find Companies Using a Technology

Returns up to `limit` companies (10 unless you say otherwise, at most 25) that use one of your technologies, with the evidence per company: how sure the match is, in how many job posts it appeared, and when it was first and last seen. A company counts only when the technology was in one of its job posts of the last 12 months, with medium or high confidence; older or weaker evidence is left out at no charge.

`technologies` takes slugs or exact names (GET /technologies finds them); an unknown one is `400` with code `unknown_technology` and the closest slugs in `suggestions`. Narrow with `countries`, `company_sizes` and `exclude_domains`: the companies you already have, so you do not pay for them again.

The call waits for the result, usually a few seconds. For more companies, send `next_cursor` back as `cursor` with the same technologies, filters and `limit`: one chain never returns a company twice. The same request on the same day, from any key of your organization (upper or lower case does not matter), returns the same search again without charging twice; with your own `Idempotency-Key` header, that key decides instead.

Some technology names are also common words or job titles in job posts (Teamleader is also "team leader"). For those, `notes` says so: check the evidence (`jobs_mentioning`, `last_seen_on`) before you tell the user a company uses it.

Each company has an `id` (`cmp_…`) that POST /tables/companies saves to the user’s Companies table, and the whole result is in mySDR at `dashboard_url`. To find the people at these companies, use Search People with `current_companies` set to their `linkedin_url`.

## 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`.

### Headers

- **`Idempotency-Key`** `string` — Optional, 8 to 255 characters. The same key and body return the first result again for 24 hours, charged once; the same key with another body is `409` `idempotency_conflict`. Without it, the same body on the same day does the same.

### Body parameters

- **`technologies`** `string[]` · **required** — 1 to 5 technologies, by slug or exact name, e.g. `["hubspot"]`. A company that uses any of them matches.
- **`countries`** `string[]` — Up to 20 countries where the company is based: ISO 3166-1 alpha-2 codes (`BE`, `NL`; `UK` is read as `GB`) or English names. Empty means anywhere.
- **`company_sizes`** `string[]` — Headcount ranges of the company; empty means any size. One of: `self-employed`, `1-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+`.
- **`exclude_domains`** `string[]` — Up to 100 domains that never come back (a website or an email address works too): your customers, or companies you already have.
- **`limit`** `integer` — How many companies to return at most, 1 to 25. Defaults to 10. You pay 3 credits per company returned, so this is also the most the call costs.
- **`cursor`** `string` — The `next_cursor` of the previous response, with the same technologies, filters and `limit`.
- **`max_credits`** `integer` — Stop before the call costs more than this: at most `max_credits` ÷ 3 companies. Below the price of one company the call is `402` `max_credits_exceeded` and nothing is searched.

## Response

- **`search_id`** `string` — The search behind this call (`srch_…`), shown in mySDR at `dashboard_url`. Reading it with GET /searches needs the Signals API; for more companies use `next_cursor`.
- **`companies`** `object[]` — The companies, in each answer the strongest evidence first (highest confidence, then most job posts). A next page can hold stronger evidence than the last company of this one. See the fields below.
- **`companies[].id`** `string` — Company id (`cmp_…`): POST /tables/companies saves it to the Companies table, for 30 days.
- **`companies[].name / domain / linkedin_url`** `string | null` — The company. `linkedin_url` is what Search People takes in `current_companies`.
- **`companies[].country_code`** `string | null` — ISO 3166-1 alpha-2 of the headquarters.
- **`companies[].industry`** `string | null` — Industry.
- **`companies[].technologies`** `object[]` — The evidence per technology you asked for: `slug`, `name`, `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).
- **`companies[].observed_at`** `string` — When this evidence was fetched (ISO 8601).
- **`companies[].credits_charged`** `integer` — What this call charged for it: 3, or 0 on a repeat of the same request.
- **`total_count`** `integer | null` — How many companies match in total, as far as the data says.
- **`has_more`** `boolean` — More companies match than this call returned.
- **`next_cursor`** `string | null` — Send it back as `cursor` for the next companies; null when there are no more.
- **`credits_used`** `integer` — Credits this call used: 0 on a repeat of the same request.
- **`truncated_by`** `string` — Only when a cap returned fewer companies than `limit`. One of: `max_credits`, `daily_limit`.
- **`sources`** `string[]` — Always `["job_postings"]`.
- **`notes`** `string[]` — What was left out and why, in plain language.
- **`dashboard_url`** `string` — These companies in mySDR (Companies, with the ones not saved yet marked).

## Credits

3 credits per company returned; no companies, no credits. 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.
- **409** `idempotency_conflict` — The Idempotency-Key was already used with another request body.
- **409** `idempotency_in_progress` — The first request with this Idempotency-Key is still being processed. Wait for the Retry-After header.

## Example

### cURL

```bash
curl -X POST "https://mysdr.app/api/v1/technologies/companies" \
  -H "Authorization: Bearer $MYSDR_API_KEY" \
  -H "Idempotency-Key: idem-1" \
  -H "Content-Type: application/json" \
  -d '{
    "technologies": [
      "hubspot"
    ],
    "countries": [
      "BE"
    ],
    "company_sizes": [
      "51-200",
      "201-500"
    ],
    "limit": 2
  }'
```

### JavaScript

```javascript
const response = await fetch('https://mysdr.app/api/v1/technologies/companies', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MYSDR_API_KEY}`,
    'Idempotency-Key': 'idem-1',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "technologies": [
      "hubspot"
    ],
    "countries": [
      "BE"
    ],
    "company_sizes": [
      "51-200",
      "201-500"
    ],
    "limit": 2
  })
});
const body = await response.json();
```

### Python

```python
import json
import os
import urllib.request

payload = {
  "technologies": [
    "hubspot"
  ],
  "countries": [
    "BE"
  ],
  "company_sizes": [
    "51-200",
    "201-500"
  ],
  "limit": 2
}

request = urllib.request.Request(
    "https://mysdr.app/api/v1/technologies/companies",
    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

```json
{
  "search_id": "srch_01JB8X3KZQ7Y9C2V5N4T6R8W0M",
  "companies": [
    {
      "id": "cmp_01JB8X3M2P4Q6R8S0T2V4W6X8Y",
      "name": "Acme Analytics",
      "domain": "acme-analytics.example",
      "linkedin_url": "https://www.linkedin.com/company/acme-analytics",
      "country_code": "BE",
      "industry": "Software Development",
      "technologies": [
        {
          "slug": "hubspot",
          "name": "HubSpot",
          "confidence": "high",
          "first_seen_on": "2024-03-12",
          "last_seen_on": "2026-10-02",
          "jobs_mentioning": 14
        }
      ],
      "observed_at": "2026-10-25T09:12:04Z",
      "credits_charged": 3
    }
  ],
  "total_count": 312,
  "has_more": true,
  "next_cursor": "cur_eyJ2IjoxLCJzIjoic3JjaF8wMUpCOFgzS1pRN1k5QzJWNU40VDZSOFcwTSJ9",
  "credits_used": 3,
  "sources": [
    "job_postings"
  ],
  "notes": [],
  "dashboard_url": "https://mysdr.app/en/gtm/companies?search=srch_01JB8X3KZQ7Y9C2V5N4T6R8W0M"
}
```
