> ## Documentation Index
> Fetch the complete documentation index at: https://docs.url2llm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Company API

> Read a company profile from its website, or find companies by name, domain and stored details.

The Company API has two synchronous operations. `POST /v1/company/profile` reads one company's brand identity, colors, logos, socials, address, contact channels, key links, legal details, technologies and pixels from its own website, with an optional measured styleguide. `POST /v1/company/search` finds companies by name or domain among stored profiles, then on the web.

## Get a company profile

Use a key with `scrape` scope.

Create an operation key once before a new request:

```bash theme={null}
export URL2LLM_OPERATION_KEY="$(uuidgen)"
```

Save that value with your task. Keep it when rerunning the script for an identical retry; do not rerun the generation command. If `uuidgen` is unavailable, set your own unique value. For a different request, generate a new value.

Node.js and Python require the saved key and use the [shared HTTP client](https://docs.url2llm.com/guides/http-clients.md). Read [retries](https://docs.url2llm.com/guides/retries.md) before retrying a request whose result you did not receive.

```bash cURL theme={null}
curl -X POST https://api.url2llm.com/v1/company/profile \
  -H "Authorization: Bearer $URL2LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${URL2LLM_OPERATION_KEY:?Set URL2LLM_OPERATION_KEY first}" \
  -d '{"domain":"example.com","styleguide":true}'
```

```javascript Node.js theme={null}
import { request } from './url2llm.mjs'

const operationKey = process.env.URL2LLM_OPERATION_KEY
if (!operationKey) throw new Error('Set URL2LLM_OPERATION_KEY to the saved operation key')
console.log('Operation key:', operationKey)
const result = await request('/v1/company/profile', {
  method: 'POST',
  operationKey,
  body: { "domain": "example.com", "styleguide": true },
})
console.log(result.data)
```

```python Python theme={null}
import os
from url2llm import request

operation_key = os.environ['URL2LLM_OPERATION_KEY']
if not operation_key:
    raise ValueError('Set URL2LLM_OPERATION_KEY to the saved operation key')
print('Operation key:', operation_key)
result = request(
    '/v1/company/profile',
    method='POST',
    operation_key=operation_key,
    body={'domain': 'example.com', 'styleguide': True},
)
print(result['data'])
```

`domain` accepts a bare domain or any URL on the company's site; IP addresses and bare public suffixes such as `co.uk` or `github.io` are rejected. The profile is keyed by the registrable domain, so `www.example.com` and `shop.example.com/about` both describe `example.com`, and the homepage of the host you send is read. `styleguide` defaults to `false`; set it to `true` to add the measured `styleguide` object for 5 more credits. `timeout` is in milliseconds, covers the whole request, defaults to 30,000 and accepts up to 120,000.

Each request reads the site fresh: the homepage, then at most four contact, about, imprint, privacy and terms pages linked from it on the same domain.

### Profile response

The request returns HTTP `200` with the profile in `data`:

```json theme={null}
{
  "success": true,
  "data": {
    "domain": "example.com",
    "url": "https://www.example.com/",
    "title": "Example",
    "description": "Payments infrastructure for the internet.",
    "primary_language": "en",
    "colors": [
      {"hex": "#635bff", "name": "Majorelle Blue", "source": "logo"},
      {"hex": "#0a2540", "name": "Prussian Blue", "source": "site"}
    ],
    "logos": [
      {
        "url": "https://www.example.com/logo.svg",
        "type": "logo",
        "mode": "light",
        "colors": [{"hex": "#635bff", "name": "Majorelle Blue"}],
        "resolution": {"width": 240, "height": 60, "aspect_ratio": 4.0}
      },
      {
        "url": "https://www.example.com/favicon.ico",
        "type": "icon",
        "mode": "has_opaque_background",
        "colors": [{"hex": "#635bff", "name": "Majorelle Blue"}],
        "resolution": {"width": 32, "height": 32, "aspect_ratio": 1.0}
      }
    ],
    "backdrops": [
      {
        "url": "https://www.example.com/og.png",
        "colors": [{"hex": "#0a2540", "name": "Prussian Blue"}],
        "resolution": {"width": 1200, "height": 630, "aspect_ratio": 1.9}
      }
    ],
    "socials": [{"type": "linkedin", "url": "https://www.linkedin.com/company/example"}],
    "address": {
      "street": "Hauptstrasse 1",
      "city": "Berlin",
      "state_province": null,
      "state_code": null,
      "postal_code": "10115",
      "country": "Deutschland",
      "country_code": "DE"
    },
    "email": "hello@example.com",
    "phone": "+4930123456",
    "links": {
      "about": "https://www.example.com/about",
      "pricing": "https://www.example.com/pricing",
      "careers": null,
      "contact": "https://www.example.com/contact",
      "blog": null,
      "docs": "https://www.example.com/docs",
      "login": null,
      "terms": "https://www.example.com/terms",
      "privacy": "https://www.example.com/privacy",
      "imprint": "https://www.example.com/imprint",
      "app_stores": {"apple": null, "google": null}
    },
    "legal": {"entity_name": "Example GmbH", "legal_form": "GmbH", "registration_id": "HRB 12345", "vat_id": "DE123456789"},
    "technologies": ["next-js", "stripe"],
    "pixels": ["google-tag-manager", "meta"],
    "styleguide": {
      "mode": "light",
      "colors": {"accent": "#635bff", "background": "#ffffff", "text": "#425466"},
      "typography": {
        "headings": {
          "h1": {"font_family": "Inter", "font_fallbacks": ["sans-serif"], "font_size": "48px", "font_weight": 700, "line_height": "56px", "letter_spacing": "-0.5px"},
          "h2": {"font_family": "Inter", "font_fallbacks": ["sans-serif"], "font_size": "32px", "font_weight": 600, "line_height": "40px", "letter_spacing": null},
          "h3": null,
          "h4": null
        },
        "p": {"font_family": "Inter", "font_fallbacks": ["sans-serif"], "font_size": "16px", "font_weight": 400, "line_height": "24px", "letter_spacing": null}
      },
      "element_spacing": {"xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "48px"},
      "shadows": {"sm": "0 1px 2px rgba(0, 0, 0, 0.1)", "md": "0 4px 12px rgba(0, 0, 0, 0.12)", "lg": "none", "xl": "none", "inner": "none"},
      "font_links": {
        "Inter": {"type": "google", "faces": [{"url": "https://fonts.gstatic.com/s/inter/v13/inter.woff2", "style": "normal", "weight": "100 900"}]}
      },
      "components": {
        "button": {
          "primary": {"background_color": "#635bff", "color": "#ffffff", "border_color": null, "border_radius": "6px", "border_width": "0px", "border_style": "none", "padding": "10px 16px", "min_width": "0px", "min_height": "40px", "font_family": "Inter", "font_size": "16px", "font_weight": 600, "text_decoration": "none", "box_shadow": null},
          "secondary": null,
          "link": {"background_color": null, "color": "#635bff", "border_color": null, "border_radius": "0px", "border_width": "0px", "border_style": "none", "padding": null, "min_width": "0px", "min_height": "0px", "font_family": "Inter", "font_size": "16px", "font_weight": 500, "text_decoration": "underline", "box_shadow": null}
        },
        "card": {"background_color": "#ffffff", "text_color": "#425466", "border_color": "#e3e8ee", "border_radius": "8px", "border_width": "1px", "border_style": "solid", "padding": "24px", "box_shadow": "0 4px 12px rgba(0, 0, 0, 0.12)"}
      }
    },
    "sources": [
      {"scrape_id": 101, "url": "https://www.example.com/", "fetched_at": "2026-11-08T10:00:00Z"},
      {"scrape_id": 102, "url": "https://www.example.com/imprint", "fetched_at": "2026-11-08T10:00:02Z"}
    ],
    "refreshed_at": "2026-11-08T10:00:00Z",
    "credits_used": 15
  },
  "request_id": "req_example"
}
```

| Field | What it describes |
| - | - |
| `domain`, `url` | The registrable domain, and the homepage after redirects |
| `title` | The brand name, preferring structured organization data and the declared site name |
| `description` | The homepage description |
| `primary_language` | A language code such as `en` |
| `colors` | Up to 6 ranked brand colors as `{hex, name, source}`. `source` is `logo` when read from logo pixels, else `site` |
| `logos` | `{url, type, mode, colors, resolution}` per image. `type` is `logo` for the lockup or `icon` for the favicon and app icons. `mode` is `light` (dark ink, for light backgrounds), `dark` (light ink) or `has_opaque_background`. `colors` are the ink colors and `resolution` is `{width, height, aspect_ratio}` |
| `backdrops` | The social sharing image and the main hero image, as `{url, colors, resolution}` |
| `socials` | `{type, url}`, one company profile link per platform. Profile sites without their own slug are `other`. Personal profiles and posts are skipped |
| `address` | `{street, city, state_province, state_code, postal_code, country, country_code}` from structured data or the imprint. `country_code` is ISO 3166-1 alpha-2 and is never guessed from the domain ending or language |
| `email`, `phone` | The first company email and phone, or `null`. Never personal contacts; see [contacts](#contacts) |
| `links` | The first homepage link to each key page on the company's own domain, plus `app_stores.apple` and `app_stores.google` |
| `legal` | Entity name, legal form, registration ID and VAT ID from structured data or the imprint |
| `technologies`, `pixels` | Site builders, frameworks, scripts and ad or analytics pixels the homepage loads, as [slugs](#technology-and-pixel-slugs) |
| `styleguide` | The measured styleguide when you send `styleguide: true`, otherwise `null`; see [styleguide](#styleguide) |
| `sources` | The pages the profile was read from, with their scrape IDs and fetch times |
| `refreshed_at` | When the homepage was fetched |
| `credits_used` | 10, or 15 with `styleguide` |

A value is `null` or empty when the site gives no signal for it. Nothing is inferred or invented.

### Styleguide

The styleguide is measured on the rendered homepage in light mode at 1920x1080. It does not reconstruct responsive layouts, dark mode or hover states.

| Field | What it describes |
| - | - |
| `mode` | `light` or `dark`, from the painted page background |
| `colors` | `{accent, background, text}`. `background` paints the most viewport area, `text` is the majority body text color and `accent` is the top brand color that is neither neutral nor the text color |
| `typography` | `headings.h1` to `headings.h4` and `p`, each `{font_family, font_fallbacks, font_size, font_weight, line_height, letter_spacing}` from the first visible element of that kind. Sizes are CSS values such as `48px` |
| `element_spacing` | `{xs, sm, md, lg, xl}` from the most common margin, padding and gap values |
| `shadows` | `{sm, md, lg, xl, inner}` as CSS `box-shadow` values ordered by blur; missing steps are `none` |
| `font_links` | Font sources by family, as `{type, faces: [{url, style, weight}]}`. `type` is `google` for Google Fonts or `custom` for `@font-face` files |
| `components` | `button.primary`, `button.secondary`, `button.link` and `card` with their colors, border, radius, padding, size, font and shadow styles |

### Contacts

`email` and `phone` are company channels, never people:

* `email`: the first address from the site's structured organization data or a linked address whose mailbox is a role such as `info`, `contact`, `hello`, `sales`, `support`, `office`, `team`, `press`, `media`, `billing`, `help`, `service`, `enquiries`, `inquiries`, `kontakt`, `admin`, `privacy`, `legal`, `jobs`, `careers`, `hr`, `marketing` or `partners`. Personal addresses such as `first.last@example.com` are left out.
* `phone`: the first number from structured data or from phone links on the homepage and contact page.

### Technology and pixel slugs

`technologies` and `pixels` in the profile, and the search filters of the same name, use these slugs:

| Field | Slugs |
| - | - |
| `technologies` | `wordpress`, `shopify`, `webflow`, `wix`, `squarespace`, `framer`, `ghost`, `drupal`, `joomla`, `hubspot`, `intercom`, `zendesk`, `drift`, `crisp`, `segment`, `hotjar`, `mixpanel`, `amplitude`, `plausible`, `stripe`, `cloudflare`, `recaptcha`, `cookiebot`, `onetrust`, `next-js`, `nuxt`, `gatsby` |
| `pixels` | `meta`, `google-analytics`, `google-tag-manager`, `google-ads`, `tiktok`, `linkedin`, `x`, `pinterest`, `snap`, `reddit`, `microsoft-ads` |

## Search companies

Use a key with `map` scope.

```bash cURL theme={null}
curl -X POST https://api.url2llm.com/v1/company/search \
  -H "Authorization: Bearer $URL2LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${URL2LLM_OPERATION_KEY:?Set URL2LLM_OPERATION_KEY first}" \
  -d '{"query":"example","limit":10}'
```

```javascript Node.js theme={null}
import { request } from './url2llm.mjs'

const operationKey = process.env.URL2LLM_OPERATION_KEY
if (!operationKey) throw new Error('Set URL2LLM_OPERATION_KEY to the saved operation key')
console.log('Operation key:', operationKey)
const result = await request('/v1/company/search', {
  method: 'POST',
  operationKey,
  body: { "query": "example", "limit": 10 },
})
console.log(result.data.results)
```

```python Python theme={null}
import os
from url2llm import request

operation_key = os.environ['URL2LLM_OPERATION_KEY']
if not operation_key:
    raise ValueError('Set URL2LLM_OPERATION_KEY to the saved operation key')
print('Operation key:', operation_key)
result = request(
    '/v1/company/search',
    method='POST',
    operation_key=operation_key,
    body={'query': 'example', 'limit': 10},
)
print(result['data']['results'])
```

`query` is a company name or domain, up to 200 characters; a URL is reduced to its registrable domain. Filters match fields of stored profiles, and every filter you send must hold:

* `country`: an ISO 3166-1 alpha-2 code.
* `technologies`, `pixels`: up to 20 [slugs](#technology-and-pixel-slugs) each, all of which must be detected, for example `["shopify"]` or `["meta"]`.
* `hasEmail`, `hasPhone`: `true` for companies with a known email or phone, `false` for those without.

Send a `query`, at least one filter, or both. `limit` defaults to 10 and accepts 1 to 20.

Stored profiles are matched first, ranked by exact domain, domain prefix, title prefix, then title. When a query has fewer stored matches than `limit` and no filter is set, one web search fills the rest. Web results are reduced to one result per company domain; social networks and domains already listed are skipped. A search with any filter returns stored companies only.

### Search response

```json theme={null}
{
  "success": true,
  "data": {
    "results": [
      {
        "domain": "example.com",
        "source": "index",
        "url": "https://example.com",
        "title": "Example",
        "snippet": "Payments infrastructure for the internet.",
        "company": {
          "title": "Example",
          "description": "Payments infrastructure for the internet.",
          "logo": "https://www.example.com/logo.svg",
          "country": "DE",
          "socials": [{"type": "linkedin", "url": "https://www.linkedin.com/company/example"}],
          "technologies": ["next-js", "stripe"],
          "pixels": ["meta"],
          "has_email": true,
          "has_phone": true,
          "refreshed_at": "2026-11-08T10:00:00Z"
        }
      },
      {
        "domain": "example.org",
        "source": "search",
        "url": "https://www.example.org/",
        "title": "Example Foundation",
        "snippet": "An example web result.",
        "company": null
      }
    ],
    "request_id": 123,
    "warning": null,
    "credits_used": 1
  },
  "request_id": "req_example"
}
```

`source` is `index` for a stored company and `search` for a web result. `company` summarizes the stored profile, or is `null` when the company has no profile yet; call [Get a company profile](#get-a-company-profile) to read one. If web search is unavailable but stored matches exist, the results hold stored companies only and `warning` explains why.

## Credits

| Operation | Credits |
| - | - |
| Company profile | 10 per successful request, or 15 with `styleguide` |
| Company search | 1 per successful request, including a search with no results |

A profile whose homepage returns a non-2xx status, is blocked, times out or is cancelled is not charged; a failed contact or legal page never fails the request. A search fails without a charge when web search is unavailable and no stored company matches. An identical idempotency key replays the stored response without another charge.

## Errors

| Status | Meaning |
| - | - |
| `400` | The site is not public, the homepage returned a non-2xx status, or the source blocked the request |
| `402` | The organization's available balance is below the request's price |
| `409` | The request with this `Idempotency-Key` was accepted, but its result is not available for replay |
| `422` | The body is invalid, for example an IP address or bare public suffix domain, or a search with neither query nor filter, or the `Idempotency-Key` was reused with a different body |
| `503` | No fetch engine could reach the site, or web search is unavailable with no stored matches |
| `504` | The request did not finish within its deadline |

Company profile shares the scrape rate limit and company search shares the map and search limit. See [errors](https://docs.url2llm.com/guides/errors.md) and [retries](https://docs.url2llm.com/guides/retries.md) before retrying.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.