---
title: "Extract component data from a product page with an LLM"
method: POST
path: "/api/admin/extract-component"
tags: ["admin-catalog-tools"]
---

# Extract component data from a product page with an LLM

`POST /api/admin/extract-component`

## Request

- Base URL: `https://voltplan.app`
- URL: `https://voltplan.app/api/admin/extract-component`
- Auth: HTTP bearer

Turns a product page into a component draft. Nothing is persisted — save the result with `POST /api/admin/save-component`. Depends on external AI and fetch services, so a request can take tens of seconds; any upstream failure ends in 500.

1. Input precedence: `productInfo.url` > `url` > `searchTerm`. A `searchTerm` without `url` first calls `POST /api/admin/search-amazon` on `NEXT_PUBLIC_BASE_URL`, forwarding the caller's `Authorization` header; if no product is found, extraction continues from the search term alone.
2. The page is fetched as markdown through Jina Reader (`https://r.jina.ai/<url>`, optional `JINA_API_KEY`, 30 s timeout) and cached in-process for 1 h (at most 50 entries).
3. Blocked or captcha Amazon pages fall back to a SearxNG search (`search.yuzuhub.com`) for the ASIN.
4. Existing brand names are read from the PocketBase `component_brands` collection.
5. The Anthropic Messages API extracts the specification (`ANTHROPIC_API_KEY` required; model `EXTRACT_MODEL`, default `claude-haiku-4-5-20251001`; up to 5 turns in which the model may call a `searxng_search` tool).
6. A second `search-amazon` call supplies one amazon.de affiliate link; without a hit the link is an amazon.de search-results URL carrying the affiliate tag.
7. http(s) icon URLs go through self-hosted background removal (BRIA RMBG-1.4 via `@huggingface/transformers`); on failure the original URL is kept.

## Request body

- AdminCatalogExtractComponentRequest: At least one of `url`, `searchTerm`, `productInfo` must be set (otherwise 400). Precedence: `productInfo.url` > `url` > `searchTerm`.
  - `url` string: Product page to crawl.
  - `searchTerm` string: Used only without `url`: resolved to a product page via `search-amazon`.
  - `productInfo` AdminCatalogExtractProductInfo: A candidate picked from `search-amazon` results. Only `url` is read; the other fields are accepted and ignored.
    - `url` string: Page to crawl.
    - `name` string
    - `description` string
    - `title` string

## Response `200`

Extracted component draft.

- AdminCatalogExtractedComponent: The LLM's JSON output, not validated server-side (fields may be missing, `null`, or additional), enriched by the server with `icon` and `affiliate_links`. Send it, optionally edited, to `save-component`.
  - `name` string
  - `type` string: Expected one of battery, charger, inverter, transformer, power, load, fuse, shunt, switch, disconnect, tool; not enforced.
  - `brand` string, nullable
  - `voltage_in` number, nullable
  - `voltage_in_type` string, nullable: `ac` or `dc`.
  - `voltage_out` number, nullable
  - `voltage_out_type` string, nullable: `ac` or `dc`.
  - `watt` number, nullable
  - `estd_price_euro` number, nullable
  - `description` string, nullable
  - `image` string, nullable
  - `icon` string, nullable: `data:image/png;base64,…` after background removal, else an http(s) image URL, else a generic `symbols/*.svg` path for battery, charger, inverter, transformer, power and load; absent or null otherwise.
  - `duty_cycle` number, nullable
  - `default_utilization_factor` number, nullable
  - `usage_hours_per_day` number, nullable
  - `amperage` number, nullable
  - `cutoff_voltage` number, nullable
  - `fuse_type` string, nullable
  - `wire_gauge` string, nullable
  - `affiliate_links` AdminCatalogExtractedAffiliateLink[], required
    - `url` string, required: amazon.de product URL with the affiliate `tag`, or an amazon.de search-results URL as fallback.
    - `country` 'universal', required
    - `isExact` boolean, required
    - `title` string, required: `Search results page` for the fallback link.

## Other responses

- `400`: None of `url`, `searchTerm`, `productInfo` was given, or `productInfo` has no `url` and no `searchTerm` was given.
- `403`: Admin access required. Returned for a missing, invalid or expired bearer token as well as for a valid token whose user lacks `is_admin=true`; admin endpoints never answer 401.
- `500`: Extraction failed; `error` carries the thrown message. Besides the examples: `Claude API error: <status> …`, `Claude did not return a final response after <n> turns`, and the parser message for a malformed JSON body.

---

[API](https://skmtc.dev/voltplan/apis/voltplan-api.md) · [All operations](https://skmtc.dev/voltplan/apis/voltplan-api/llms.txt) · [OpenAPI document](https://skmtc.dev/voltplan/apis/voltplan-api/revisions/575b86a7af92?raw)
