# API Keys (/api-keys)
API keys authenticate your requests to the API. You manage them entirely from the dashboard: set a category, restrict usage with rolling quotas, add an expiry date, and monitor activity per key through request logs.
For how to include a key in a request, see the [Introduction](/introduction).
## Creating a key [#creating-a-key]
### Open the form [#open-the-form]
Go to [dashboard → API Keys → New](https://app.piloterr.com).
### Fill in the fields [#fill-in-the-fields]
Give the key an **alias** (unique label for your own reference), pick a **category**, and optionally configure quotas and an expiry date.
### Copy the secret [#copy-the-secret]
After saving, the secret is shown **once**. Copy it immediately and store it in an environment variable. It cannot be retrieved again.
## Key fields [#key-fields]
| Field | Description |
| ------------------ | ------------------------------------------------------------------------------------------------ |
| **Alias** | Unique label per account. Shows up in logs and dashboard filters. |
| **Category** | Organizational tag (see [Categories](#categories)). Has no effect on API behavior. |
| **Active** | Enables or disables the key. Inactive keys are rejected immediately with `401 Inactive API Key`. |
| **Expires** | Optional date after which the key stops working. Leave blank for no expiry. |
| **Request quotas** | Optional per-key caps on rolling time windows (see [Per-key quotas](#per-key-quotas)). |
## Categories [#categories]
Categories tag keys for your own organization. They appear in dashboard filters and analytics but do not change how the API processes a request.
| Category | Typical use |
| ------------- | --------------------------------------------------- |
| `production` | Live traffic, customer-facing systems |
| `development` | Local development and testing |
| `staging` | Pre-production and CI environments |
| Custom | Any free-text label, e.g. `sandbox`, `partner-acme` |
Keep one key per environment so you can revoke or rotate it independently.
## Per-key quotas [#per-key-quotas]
Each key can have independent hard caps on request volume, enforced on rolling windows (not calendar boundaries):
| Quota | Window |
| ----------- | ----------------------------------------------------- |
| **Total** | All-time: the key is blocked permanently once reached |
| **Daily** | Last 24 hours (rolling, not midnight-to-midnight) |
| **Weekly** | Last 7 days (rolling) |
| **Monthly** | Last 30 days (rolling) |
Leave a field blank to apply no cap for that window. When a quota is reached, the API returns `401` with `Rate limit exceeded for the API key:` and the quota name in the message:
```json
{ "error": "Rate limit exceeded for the API key: quota total" }
```
```json
{ "error": "Rate limit exceeded for the API key: quota daily" }
```
```json
{ "error": "Rate limit exceeded for the API key: quota weekly" }
```
```json
{ "error": "Rate limit exceeded for the API key: quota monthly" }
```
Quota counts are aggregated from request logs and cached for up to 24 hours. Enforcement is accurate within that window, not to the exact request.
Per-key quotas are distinct from **plan-level rate limits** (per-second and per-minute caps that apply account-wide). Both return `401`, with a message that names the limit you reached. See [Error Handling](/error-handling) for details.
## Expiry [#expiry]
Set an expiry date on keys used in scripts, CI pipelines, or partner integrations. Once the date passes, the API returns:
```json
{ "error": "Expired API Key" }
```
HTTP status `401`. Create and deploy a replacement key before the expiry date to avoid any downtime.
## Request logs [#request-logs]
Every request made with a key is recorded under **API Keys → Logs** in the dashboard. Each entry shows:
| Field | Description |
| -------- | -------------------------------- |
| Endpoint | HTTP method and path |
| Status | Response status code |
| Duration | Time to first byte (ms) |
| IP | Caller IP address |
| Credits | Credits charged for this request |
Use logs to audit activity per key, debug unexpected errors, or identify traffic from compromised keys.
## Deactivating vs. deleting [#deactivating-vs-deleting]
| Action | Effect |
| -------------- | ---------------------------------------------------------------------------------------------------- |
| **Deactivate** | Key is blocked immediately. Usage history and settings are kept. Can be reactivated. |
| **Delete** | Permanently removes the key and its configuration. Only possible if the key has **never been used**. |
Prefer deactivation over deletion. If a key has request history, it cannot be deleted. The history is retained for billing and auditing.
## Rotating a key [#rotating-a-key]
### Create the replacement [#create-the-replacement]
Go to [dashboard → API Keys → New](https://app.piloterr.com) and create a new key with the same category and quota settings. Give it a new alias to distinguish it.
### Deploy the new key [#deploy-the-new-key]
Update the environment variable in every system that uses the old key and redeploy.
### Deactivate the old key [#deactivate-the-old-key]
Set the old key to **inactive** in the dashboard. This blocks any remaining requests without removing its history.
## Error reference [#error-reference]
| Error | Status | Cause |
| ---------------------------------------------------------------------------- | ------ | ---------------------------------------------- |
| `Invalid API Key` | 401 | Key not found or malformed |
| `Inactive API Key` | 401 | Key is disabled |
| `Expired API Key` | 401 | Key has passed its expiry date |
| `Rate limit exceeded for the API key: quota …` | 401 | Per-key rolling quota reached |
| `Rate limit exceeded: You've exceeded the … rate limit on your subscription` | 401 | Plan-level per-second or per-minute cap |
| `IP temporarily blocked` | 403 | Too many failed auth attempts from the same IP |
***
See also: [Best Practices](/best-practices) for security guidelines and [Glossary](/glossary) for term definitions.
# Auto Top-Up (/auto-top-up)
Never run out of capacity mid-workflow. Auto Top-Up monitors your remaining balance (API quota + extra credits) and purchases a selected one-time pack on your behalf when your balance drops below a configurable threshold.
## How it works [#how-it-works]
### Set a threshold [#set-a-threshold]
Choose the percentage of your remaining balance (API quota + extra credits) at which the automatic purchase should trigger. When your balance falls below this level, Auto Top-Up activates.
### Pick a one-time pack [#pick-a-one-time-pack]
Select the one-time pack to purchase automatically. Depending on the product, this adds API quota or extra credits. The pack is charged to the payment method on file in your account.
### Capacity is added instantly [#capacity-is-added-instantly]
Once the purchase completes, the API quota or extra credits are available immediately. No downtime, no failed requests.
## Enabling Auto Top-Up [#enabling-auto-top-up]
1. Go to **[Settings → Subscription](https://app.piloterr.com)** in your dashboard.
2. Open the **Auto Top-Up** section.
3. Toggle **Enable** and choose a one-time pack (API top-up or extra credits) and a threshold percentage.
4. Click **Save** to activate.
Auto Top-Up is only available on paid plans. Users on the Free plan cannot enable automatic purchases.
## Configuration options [#configuration-options]
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **One-time pack** | The product purchased automatically when the threshold is reached (API top-up or extra credits). |
| **Threshold (%)** | The percentage of your remaining balance (API quota + extra credits) that triggers the top-up. Accepts values between 10% and 30%. |
## Billing [#billing]
Automatic purchases are charged to the payment method associated with your account. You will receive an invoice by email for each automatic purchase, just like a manual credit purchase.
Make sure your payment method is up to date. If a charge fails, Auto Top-Up is automatically **disabled** on your account to prevent repeated failed attempts. You will need to re-enable it manually once your payment method is updated.
## Built-in protections [#built-in-protections]
Auto Top-Up includes two safeguards to prevent unexpected charges:
| Protection | How it works |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Threshold-only trigger** | A purchase only fires when your remaining balance drops below the configured percentage. It never runs on a fixed schedule or at a specific time. |
| **72-hour cooldown** | After a top-up completes, the system waits at least 72 hours before it can trigger again, even if your balance drops below the threshold a second time during that window. |
| **Auto-disable on failure** | If the Stripe charge fails, Auto Top-Up is automatically disabled to protect your account from repeated failed payment attempts. |
## Frequently asked questions [#frequently-asked-questions]
### Will I be charged immediately when I enable Auto Top-Up? [#will-i-be-charged-immediately-when-i-enable-auto-top-up]
No. Enabling Auto Top-Up does not trigger a purchase. The charge only fires when your balance actually drops below the configured threshold.
### Can I be charged multiple times in a short period? [#can-i-be-charged-multiple-times-in-a-short-period]
No. A built-in 72-hour cooldown prevents more than one automatic purchase from firing within any 72-hour window, regardless of how your balance fluctuates.
### Can I disable Auto Top-Up at any time? [#can-i-disable-auto-top-up-at-any-time]
Yes. Toggle the **Enable** switch off in **Settings → Subscription → Auto Top-Up** and save. No further automatic purchases will occur.
### What happens if my payment fails? [#what-happens-if-my-payment-fails]
Auto Top-Up is automatically disabled on your account. You will need to update your payment method and re-enable the feature manually from your subscription settings.
### Is Auto Top-Up available on the Free plan? [#is-auto-top-up-available-on-the-free-plan]
No. You need an active paid subscription to enable automatic credit top-ups.
# Best Practices (/best-practices)
Follow these guidelines to build integrations that are reliable, cost-efficient, and easy to maintain.
## Credit efficiency [#credit-efficiency]
### Check your balance before large batches [#check-your-balance-before-large-batches]
Before triggering a high-volume operation, verify you have enough credits. The [Usage](/usage) endpoint is free and returns your current balance in real time.
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: YOUR_API_KEY"
```
Enable [Auto Top-Up](/auto-top-up) to automatically refill your balance when it drops below a threshold, so your workflows keep running without interruption.
### Avoid redundant calls [#avoid-redundant-calls]
Cache results on your side whenever the underlying data is unlikely to change between requests. Re-fetching identical inputs wastes credits.
```typescript
const cache = new Map()
async function fetchCached(key: string, fetcher: () => Promise) {
if (cache.has(key)) return cache.get(key)
const result = await fetcher()
cache.set(key, result)
return result
}
```
### Only call what you need [#only-call-what-you-need]
Each endpoint has a defined credit cost listed on the [Credits](/credits) page. Prefer lighter endpoints when a full response is not required.
***
## Authentication [#authentication]
### Store keys in environment variables [#store-keys-in-environment-variables]
Never hardcode your API key in source code. Use environment variables and load them at runtime.
```typescript
const apiKey = process.env.PILOTERR_API_KEY
if (!apiKey) throw new Error("API key is not set")
```
```python
import os
api_key = os.environ["PILOTERR_API_KEY"]
```
```php
$apiKey = getenv('PILOTERR_API_KEY');
```
```go
apiKey := os.Getenv("PILOTERR_API_KEY")
```
### Use separate keys per environment [#use-separate-keys-per-environment]
Create distinct API keys for development, staging, and production in your [dashboard](https://app.piloterr.com). This lets you revoke or rotate a compromised key without affecting other environments.
| Environment | Recommended category |
| ------------ | -------------------- |
| Local dev | `development` |
| CI / staging | `development` |
| Production | `production` |
### Rotate keys periodically [#rotate-keys-periodically]
Treat API keys like passwords. Set an expiry date on sensitive keys and rotate them on a regular schedule from the API Keys section of your account settings.
Never expose your API key in client-side code, browser requests, or public repositories. Always proxy calls through your own backend.
***
## Error handling [#error-handling]
### Always check the HTTP status code [#always-check-the-http-status-code]
Do not assume a request succeeded. Every response should be checked against its HTTP status code before processing the body. See the [Error Handling](/error-handling) guide for a full breakdown.
### Retry on transient errors [#retry-on-transient-errors]
Server errors (`500`) and network timeouts are transient. Implement exponential backoff to retry automatically:
```typescript
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const res = await fetch(url, options)
if (res.status === 500 && attempt < maxRetries) {
await new Promise(r => setTimeout(r, 500 * 2 ** attempt))
continue
}
return res
} catch {
if (attempt === maxRetries) throw new Error("Max retries reached")
await new Promise(r => setTimeout(r, 500 * 2 ** attempt))
}
}
}
```
### Never retry on `402` without topping up [#never-retry-on-402-without-topping-up]
A `402` with `Payment required`, an open-invoice message, or `Insufficient credits: …` means the call was not executed. Retrying immediately will not help. Add capacity from your [dashboard](https://app.piloterr.com) (top-up or plan upgrade), settle open invoices, or enable [Auto Top-Up](/auto-top-up).
***
## Rate limits [#rate-limits]
### Spread requests over time [#spread-requests-over-time]
If you need to send a large number of calls, distribute them across time rather than firing them all at once. A simple delay between iterations prevents hitting rate limits.
```typescript
const DELAY_MS = 100 // adjust based on your plan
for (const item of items) {
await processItem(item)
await new Promise(r => setTimeout(r, DELAY_MS))
}
```
### Monitor the `401` `Rate limit exceeded` response [#monitor-the-401-rate-limit-exceeded-response]
When rate-limited, the API returns `401` with a `Rate limit exceeded…` message (plan per-second/per-minute caps or a per-key quota). Back off and wait before retrying. Consider upgrading your plan or raising the key's quotas if you hit this regularly. The public API does **not** use HTTP `429` for these limits.
***
## Security [#security]
### Never make API calls from the browser [#never-make-api-calls-from-the-browser]
Your API key would be visible to anyone who inspects the network tab. Always route calls through your own server.
```
Browser → Your backend → Piloterr API
```
### Validate and sanitize inputs [#validate-and-sanitize-inputs]
If your application accepts user-supplied parameters that are passed to the API (URLs, search queries, etc.), sanitize them to prevent injection or unexpected behavior.
***
## Monitoring [#monitoring]
### Track credit consumption [#track-credit-consumption]
Query the [Usage](/usage) endpoint on a schedule (e.g. daily) and alert when consumption exceeds a threshold. This prevents surprises at the end of a billing period.
| Metric | Why it matters |
| --------------------------- | -------------------------------- |
| `remaining` | Warns you before credits run out |
| `subscription.percent_used` | Tracks your plan utilization |
| `credits.remaining` | Monitors extra credit balance |
### Log request context [#log-request-context]
Store the HTTP status code and a timestamp for every API call. This makes it easy to audit credit consumption and debug failures after the fact.
```typescript
async function apiCall(endpoint: string, params: Record) {
const url = new URL(`https://api.piloterr.com${endpoint}`)
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))
const res = await fetch(url.toString(), {
headers: { "x-api-key": process.env.API_KEY! },
})
console.log(JSON.stringify({
endpoint,
status: res.status,
// Sync success is HTTP 200. Async accept is HTTP 202 (not billed);
// async completion is billed as 201 in your request logs, not as an HTTP response.
billed: res.status === 200,
timestamp: new Date().toISOString(),
}))
return res
}
```
***
## Checklist [#checklist]
Use this checklist before going to production:
* [ ] API key stored in an environment variable, not in source code
* [ ] Separate keys for development and production
* [ ] HTTP status codes checked on every response
* [ ] Retry logic with exponential backoff for `500` errors
* [ ] No retries on `402` without adding credits first
* [ ] Rate limit handling in place
* [ ] Credit balance monitored and alerts configured
* [ ] Auto Top-Up enabled (or a manual top-up process defined)
* [ ] All API calls proxied through your backend
***
See also: [Stats](/stats) for infrastructure health, [Error Handling](/error-handling) for a complete list of status codes, and [Glossary](/glossary) for term definitions.
# Credits (/credits)
Each API call consumes credits from your account based on the endpoint cost. The table below lists every endpoint and its cost per successful request.
## Account balances [#account-balances]
Your account tracks two separate balances:
**API quota** combines your subscription allowance (renewed each billing period) and any remaining one-time top-ups. Subscription quota resets each billing period; top-ups are purchased once and consumed over time—they do not reset monthly. Calls consume subscription quota first, then top-ups.
**Extra credits** are a separate balance used only after API quota (subscription + top-ups) is fully exhausted. They do not reset monthly. You may receive them from onboarding rewards or manual extra-credit purchases.
Monitor both balances from the [Usage](/usage) endpoint or your dashboard sidebar. See the [Glossary](/glossary) for full definitions.
**Synchronous calls** are billed on `200`. **Asynchronous calls** (`is_async=true`) return `202` (not billed) and are billed when the job completes successfully — recorded as `201` in your request logs. Polling `GET /jobs/{job_id}` is always free. Failed jobs (`500`) are never billed. `404` billing varies by endpoint (check each endpoint page). All other 4xx/5xx responses are free.
| Endpoint | Method | Path | Credits |
| --------------------------------------------------------------- | ------ | ----------------------------------- | ------- |
| [Action Product](/action-product) | `GET` | `/v2/action/product` | 2 |
| [Action Search](/action-search) | `GET` | `/v2/action/search` | 2 |
| [Aldi Product](/aldi-product) | `GET` | `/v2/aldi/product` | 1 |
| [Aldi Search](/aldi-search) | `GET` | `/v2/aldi/search` | 1 |
| [Alibaba Product](/alibaba-product) | `GET` | `/v2/alibaba/product` | 2 |
| [Alibaba Search](/alibaba-search) | `GET` | `/v2/alibaba/search` | 1 |
| [AliExpress Product](/aliexpress-product) | `GET` | `/v2/aliexpress/product` | 2 |
| [AliExpress Search](/aliexpress-search) | `GET` | `/v2/aliexpress/search` | 2 |
| [AliExpress Store Product](/aliexpress-store-product) | `GET` | `/v2/aliexpress/store/product` | 2 |
| [Allegro Product](/allegro-product) | `GET` | `/v2/allegro/product` | 3 |
| [Allegro Search](/allegro-search) | `GET` | `/v2/allegro/search` | 3 |
| [Amazon Product](/amazon-product) | `GET` | `/v2/amazon/product` | 1 |
| [Amazon Product Offer](/amazon-product-offer) | `GET` | `/v2/amazon/product/offer` | 1 |
| [Amazon Search](/amazon-search) | `GET` | `/v2/amazon/search` | 1 |
| [Apartments Property](/apartments-property) | `GET` | `/v2/apartments/property` | 2 |
| [Apartments Search](/apartments-search) | `GET` | `/v2/apartments/search` | 2 |
| [Auchan Product](/auchan-product) | `GET` | `/v2/auchan/product` | 2 |
| [Auchan Search](/auchan-search) | `GET` | `/v2/auchan/search` | 2 |
| [Autodoc Product](/autodoc-product) | `GET` | `/v2/autodoc/product` | 2 |
| [Autodoc Product Related](/autodoc-product-related) | `GET` | `/v2/autodoc/product/related` | 2 |
| [Autodoc Search](/autodoc-search) | `GET` | `/v2/autodoc/search` | 2 |
| [AutoScout24 Ad](/autoscout24-ad) | `GET` | `/v2/autoscout24/ad` | 1 |
| [AutoScout24 Search](/autoscout24-search) | `GET` | `/v2/autoscout24/search` | 1 |
| [Autotrader Ad](/autotrader-ad) | `GET` | `/v2/autotrader/ad` | 2 |
| [Autotrader Search](/autotrader-search) | `GET` | `/v2/autotrader/search` | 2 |
| [Best Buy Product](/bestbuy-product) | `GET` | `/v2/bestbuy/product` | 2 |
| [Best Buy Search](/bestbuy-search) | `GET` | `/v2/bestbuy/search` | 2 |
| [Bing Search](/bing-search) | `GET` | `/v2/bing/search` | 1 |
| [Brave Search](/brave-search) | `GET` | `/v2/brave/search` | 1 |
| [Carrefour Product](/carrefour-product) | `GET` | `/v2/carrefour/product` | 2 |
| [Carrefour Search](/carrefour-search) | `GET` | `/v2/carrefour/search` | 2 |
| [Carrefour Suggest](/carrefour-suggest) | `GET` | `/v2/carrefour/suggest` | 1 |
| [Cdiscount Product](/cdiscount-product) | `GET` | `/v2/cdiscount/product` | 2 |
| [Cdiscount Product Offer](/cdiscount-product-offer) | `GET` | `/v2/cdiscount/product/offer` | 2 |
| [Cdiscount Search](/cdiscount-search) | `GET` | `/v2/cdiscount/search` | 2 |
| [Chewy Product](/chewy-product) | `GET` | `/v2/chewy/product` | 2 |
| [Chewy Products Finder](/finder-chewy-products) | `GET` | `/v2/finder/chewy-products` | 0 |
| [Chewy Search](/chewy-search) | `GET` | `/v2/chewy/search` | 2 |
| [Company Database](/company) | `GET` | `/v2/company` | 1 |
| [Copart Lot](/copart-lot) | `GET` | `/v2/copart/lot` | 2 |
| [Copart Search](/copart-search) | `GET` | `/v2/copart/search` | 2 |
| [Costco Product](/costco-product) | `GET` | `/v2/costco/product` | 2 |
| [Costco Search](/costco-search) | `GET` | `/v2/costco/search` | 2 |
| [Crunchbase Company Info](/crunchbase-company-info) | `GET` | `/v2/crunchbase/company/info` | 2 |
| [Crunchbase Event](/crunchbase-event) | `GET` | `/v2/crunchbase/event` | 2 |
| [Crunchbase Funding Round](/crunchbase-funding-round) | `GET` | `/v2/crunchbase/funding_round` | 2 |
| [Crunchbase Funding Rounds](/crunchbase-funding-rounds) | `GET` | `/v2/crunchbase/funding_rounds` | 2 |
| [Crunchbase People Info](/crunchbase-people-info) | `GET` | `/v2/crunchbase/people/info` | 2 |
| [Crunchbase Search](/crunchbase-search) | `GET` | `/v2/crunchbase/search` | 2 |
| [Dataset Linkedin Company](/datasets-linkedin-company) | `POST` | `/v2/datasets/linkedin/company` | 1 |
| [DigiKey Product](/digikey-product) | `GET` | `/v2/digikey/product` | 2 |
| [DigiKey Search](/digikey-search) | `GET` | `/v2/digikey/search` | 2 |
| [Domain DNSBL](/domain-dnsbl) | `GET` | `/v2/domain/dnsbl` | 1 |
| [Domain Malicious](/domain-malicious) | `GET` | `/v2/domain/malicious` | 1 |
| [Domain Whois](/domain-whois) | `GET` | `/v2/domain/whois` | 1 |
| [E.Leclerc Product](/eleclerc-product) | `GET` | `/v2/eleclerc/product` | 1 |
| [E.Leclerc Search](/eleclerc-search) | `GET` | `/v2/eleclerc/search` | 1 |
| [E.Leclerc Suggest](/eleclerc-suggest) | `GET` | `/v2/eleclerc/suggest` | 1 |
| [El Corte Inglés Product](/elcorteingles-product) | `GET` | `/v2/elcorteingles/product` | 3 |
| [El Corte Inglés Search](/elcorteingles-search) | `GET` | `/v2/elcorteingles/search` | 3 |
| [Eleclerc Stores Finder](/finder-eleclerc-stores) | `GET` | `/v2/finder/eleclerc-stores` | 0 |
| [Email Analyzes](/email-analyzes) | `GET` | `/v2/email/analyzes` | 1 |
| [Email Finder](/email-finder) | `GET` | `/v2/email/finder` | 1 |
| [Email Verify](/email-verify) | `GET` | `/v2/email/verify` | 1 |
| [Expedia Ad](/expedia-ad) | `GET` | `/v2/expedia/ad` | 2 |
| [Expedia Search](/expedia-search) | `GET` | `/v2/expedia/search` | 2 |
| [Fiverr Profile Info](/fiverr-profile-info) | `GET` | `/v2/fiverr/profile/info` | 1 |
| [Fiverr Search](/fiverr-search) | `GET` | `/v2/fiverr/search` | 1 |
| [G2 Product Info](/g2-product-info) | `GET` | `/v2/g2/product/info` | 2 |
| [GitHub User Info](/github-user-info) | `GET` | `/v2/github/user/info` | 1 |
| [Google Countries Finder](/finder-google-countries) | `GET` | `/v2/finder/google-countries` | 0 |
| [Google Images](/google-images) | `GET` | `/v2/google/images` | 1 |
| [Google Jobs](/google-jobs) | `GET` | `/v2/google/jobs` | 1 |
| [Google Languages Finder](/finder-google-languages) | `GET` | `/v2/finder/google-languages` | 0 |
| [Google Locations Finder](/finder-google-locations) | `GET` | `/v2/finder/google-locations` | 0 |
| [Google News](/google-news) | `GET` | `/v2/google/news` | 1 |
| [Google Search](/google-search) | `GET` | `/v2/google/search` | 1 |
| [Google Search Autocomplete](/google-search-autocomplete) | `GET` | `/v2/google/search/autocomplete` | 1 |
| [Google Videos](/google-videos) | `GET` | `/v2/google/videos` | 1 |
| [Grainger Product](/grainger-product) | `GET` | `/v2/grainger/product` | 2 |
| [Grainger Search](/grainger-search) | `GET` | `/v2/grainger/search` | 2 |
| [Homestra Ad](/homestra-ad) | `GET` | `/v2/homestra/ad` | 1 |
| [Homestra Search](/homestra-search) | `GET` | `/v2/homestra/search` | 1 |
| [Idealista Property](/idealista-property) | `GET` | `/v2/idealista/property` | 2 |
| [Idealista Search](/idealista-search) | `GET` | `/v2/idealista/search` | 2 |
| [IKEA Product](/ikea-product) | `GET` | `/v2/ikea/product` | 1 |
| [IKEA Search](/ikea-search) | `GET` | `/v2/ikea/search` | 1 |
| [Immowelt Property](/immowelt-property) | `GET` | `/v2/immowelt/property` | 1 |
| [Immowelt Search](/immowelt-search) | `GET` | `/v2/immowelt/search` | 1 |
| [Indeed Company Info](/indeed-company-info) | `GET` | `/v2/indeed/company/info` | 2 |
| [Indeed Company Search](/indeed-company-search) | `GET` | `/v2/indeed/company/search` | 2 |
| [Indeed Job Info](/indeed-job-info) | `GET` | `/v2/indeed/job/info` | 2 |
| [Indeed Job Salary](/indeed-job-salary) | `GET` | `/v2/indeed/job/salary` | 2 |
| [Indeed Job Search](/indeed-job-search) | `GET` | `/v2/indeed/job/search` | 2 |
| [Instagram Post Info](/instagram-post-info) | `GET` | `/v2/instagram/post/info` | 1 |
| [Instagram User Info](/instagram-user-info) | `GET` | `/v2/instagram/user/info` | 1 |
| [Kick User Info](/kick-user-info) | `GET` | `/v2/kick/user/info` | 1 |
| [Leboncoin Ad](/leboncoin-ad) | `GET` | `/v2/leboncoin/ad` | 1 |
| [Leboncoin Search](/leboncoin-search) | `GET` | `/v2/leboncoin/search` | 1 |
| [Leboncoin Search API](/leboncoin-search-api) | `POST` | `/v2/leboncoin/search_api` | 1 |
| [Leroy Merlin Product](/leroymerlin-product) | `GET` | `/v2/leroymerlin/product` | 3 |
| [Leroy Merlin Products Finder](/finder-leroymerlin-products) | `GET` | `/v2/finder/leroymerlin-products` | 0 |
| [Leroy Merlin Search](/leroymerlin-search) | `GET` | `/v2/leroymerlin/search` | 3 |
| [Leroy Merlin Stores Finder](/finder-leroymerlin-stores) | `GET` | `/v2/finder/leroymerlin-stores` | 0 |
| [Lidl Product](/lidl-product) | `GET` | `/v2/lidl/product` | 1 |
| [Lidl Search](/lidl-search) | `GET` | `/v2/lidl/search` | 1 |
| [LinkedIn Company Info](/linkedin-company-info) | `GET` | `/v2/linkedin/company/info` | 1 |
| [LinkedIn Industries Finder](/finder-linkedin-industries) | `GET` | `/v2/finder/linkedin-industries` | 0 |
| [LinkedIn Job Count](/linkedin-job-count) | `GET` | `/v2/linkedin/job/count` | 1 |
| [LinkedIn Job Info](/linkedin-job-info) | `GET` | `/v2/linkedin/job/info` | 1 |
| [LinkedIn Job Search](/linkedin-job-search) | `GET` | `/v2/linkedin/job/search` | 1 |
| [LinkedIn Job Suggest](/linkedin-job-suggest) | `GET` | `/v2/linkedin/job/suggest` | 1 |
| [LinkedIn Post Info](/linkedin-post-info) | `GET` | `/v2/linkedin/post/info` | 1 |
| [LinkedIn Product Info](/linkedin-product-info) | `GET` | `/v2/linkedin/product/info` | 1 |
| [LinkedIn Profile Info](/linkedin-profile-info) | `GET` | `/v2/linkedin/profile/info` | 1 |
| [Maersk Tracking](/maersk-tracking) | `GET` | `/v2/maersk/tracking` | 2 |
| [ManoMano Product](/manomano-product) | `GET` | `/v2/manomano/product` | 1 |
| [ManoMano Product Offer](/manomano-product-offer) | `GET` | `/v2/manomano/product/offer` | 1 |
| [ManoMano Search](/manomano-search) | `GET` | `/v2/manomano/search` | 1 |
| [Mascus Ad](/mascus-ad) | `GET` | `/v2/mascus/ad` | 1 |
| [Mascus Search](/mascus-search) | `GET` | `/v2/mascus/search` | 1 |
| [Mobile Ad](/mobile-ad) | `GET` | `/v2/mobile/ad` | 3 |
| [Mobile Search](/mobile-search) | `GET` | `/v2/mobile/search` | 3 |
| [Mr.Bricolage Stores Finder](/finder-mrbricolage-stores) | `GET` | `/v2/finder/mrbricolage-stores` | 0 |
| [Mr.Bricolage Suggest](/mrbricolage-suggest) | `GET` | `/v2/mrbricolage/suggest` | 3 |
| [Newegg Product](/newegg-product) | `GET` | `/v2/newegg/product` | 2 |
| [Newegg Search](/newegg-search) | `GET` | `/v2/newegg/search` | 2 |
| [Oceanio Search](/oceanio-search) | `GET` | `/v2/oceanio/search` | 1 |
| [Oscaro Product](/oscaro-product) | `GET` | `/v2/oscaro/product` | 2 |
| [Oscaro Search](/oscaro-search) | `GET` | `/v2/oscaro/search` | 2 |
| [Ovoko Ad](/ovoko-ad) | `GET` | `/v2/ovoko/ad` | 1 |
| [Ovoko Search](/ovoko-search) | `GET` | `/v2/ovoko/search` | 1 |
| [Owler Company Info](/owler-company-info) | `GET` | `/v2/owler/company/info` | 1 |
| [Owler Search](/owler-search) | `GET` | `/v2/owler/search` | 1 |
| [PagesJaunes Page Info](/pagesjaunes-page-info) | `GET` | `/v2/pagesjaunes/page/info` | 1 |
| [PagesJaunes Search](/pagesjaunes-search) | `GET` | `/v2/pagesjaunes/search` | 1 |
| [Patreon Profile Info](/patreon-profile-info) | `GET` | `/v2/patreon/profile/info` | 1 |
| [Patreon Search](/patreon-search) | `GET` | `/v2/patreon/search` | 1 |
| [Picard Product](/picard-product) | `GET` | `/v2/picard/product` | 1 |
| [Picard Search](/picard-search) | `GET` | `/v2/picard/search` | 1 |
| [Pinterest User Info](/pinterest-user-info) | `GET` | `/v2/pinterest/user/info` | 1 |
| [PitchBook Company Info](/pitchbook-company-info) | `GET` | `/v2/pitchbook/company/info` | 2 |
| [PitchBook Search](/pitchbook-search) | `GET` | `/v2/pitchbook/search` | 2 |
| [Point.P Product](/pointp-product) | `GET` | `/v2/pointp/product` | 2 |
| [Point.P Search](/pointp-search) | `GET` | `/v2/pointp/search` | 2 |
| [ProductHunt Product Info](/producthunt-product-info) | `GET` | `/v2/producthunt/product/info` | 1 |
| [Realtor Property](/realtor-property) | `GET` | `/v2/realtor/property` | 2 |
| [Realtor Search](/realtor-search) | `GET` | `/v2/realtor/search` | 2 |
| [Reverb Product](/reverb-product) | `GET` | `/v2/reverb/product` | 2 |
| [Reverb Search](/reverb-search) | `GET` | `/v2/reverb/search` | 2 |
| [Rexel Product](/rexel-product) | `GET` | `/v2/rexel/product` | 2 |
| [Rexel Search](/rexel-search) | `GET` | `/v2/rexel/search` | 2 |
| [SeLoger Agency](/seloger-agency) | `GET` | `/v2/seloger/agency` | 2 |
| [SeLoger Agency Rent](/seloger-agency-rent) | `GET` | `/v2/seloger/agency/rent` | 2 |
| [SeLoger Agency Sale](/seloger-agency-sale) | `GET` | `/v2/seloger/agency/sale` | 2 |
| [SeLoger Property](/seloger-property) | `GET` | `/v2/seloger/property` | 2 |
| [SeLoger Search](/seloger-search) | `GET` | `/v2/seloger/search` | 2 |
| [Shophouzz Product](/shophouzz-product) | `GET` | `/v2/shophouzz/product` | 1 |
| [Shopify Apps](/shopify-apps) | `GET` | `/v2/shopify/apps` | 1 |
| [Shopify Product](/shopify-product) | `GET` | `/v2/shopify/product` | 1 |
| [SimilarWeb Domain](/similarweb-domain) | `GET` | `/v2/similarweb/domain` | 1 |
| [Similarweb Search](/similarweb-search) | `GET` | `/v2/similarweb/search` | 1 |
| [SnapEDA Product](/snapeda-product) | `GET` | `/v2/snapeda/product` | 2 |
| [SnapEDA Search](/snapeda-search) | `GET` | `/v2/snapeda/search` | 2 |
| [Stats](/stats) | `GET` | `/v2/stats` | 0 |
| [StockX Product](/stockx-product) | `GET` | `/v2/stockx/product` | 2 |
| [StockX Search](/stockx-search) | `GET` | `/v2/stockx/search` | 2 |
| [StockX Trends](/stockx-trends) | `GET` | `/v2/stockx/trends` | 2 |
| [Target Product](/target-product) | `GET` | `/v2/target/product` | 2 |
| [Target Search](/target-search) | `GET` | `/v2/target/search` | 2 |
| [TheFork Restaurant](/thefork-restaurant) | `GET` | `/v2/thefork/restaurant` | 2 |
| [TheFork Search](/thefork-search) | `GET` | `/v2/thefork/search` | 2 |
| [Thomasnet Company](/thomasnet-company) | `GET` | `/v2/thomasnet/company` | 3 |
| [Thomasnet Search](/thomasnet-search) | `GET` | `/v2/thomasnet/search` | 3 |
| [Trustpilot Company Info](/trustpilot-company-info) | `GET` | `/v2/trustpilot/company/info` | 1 |
| [Upwork Freelancer](/upwork-freelancer) | `GET` | `/v2/upwork/freelancer` | 2 |
| [Upwork Jobs](/upwork-jobs) | `GET` | `/v2/upwork/jobs` | 2 |
| [Upwork Services](/upwork-services) | `GET` | `/v2/upwork/services` | 2 |
| [Upwork Services Info](/upwork-services-info) | `GET` | `/v2/upwork/services/info` | 2 |
| [Usage](/usage) | `GET` | `/v2/usage` | 0 |
| [Vinted Item](/vinted-item) | `GET` | `/v2/vinted/item` | 1 |
| [Vinted Search](/vinted-search) | `GET` | `/v2/vinted/search` | 1 |
| [Vinted User Info](/vinted-user-info) | `GET` | `/v2/vinted/user/info` | 1 |
| [Walmart Product](/walmart-product) | `GET` | `/v2/walmart/product` | 2 |
| [Walmart Search](/walmart-search) | `GET` | `/v2/walmart/search` | 2 |
| [Walmart Suggest](/walmart-suggest) | `GET` | `/v2/walmart/suggest` | 2 |
| [Website Antibot](/website-antibot) | `GET` | `/v2/website/antibot` | 1 |
| [Website Crawler](/website-crawler) | `GET` | `/v2/website/crawler` | 1 |
| [Website Email Phone Extractor](/website-email-phone-extractor) | `GET` | `/v2/website/email_phone_extractor` | 1 |
| [Website Rendering](/website-rendering) | `GET` | `/v2/website/rendering` | 2 |
| [Website Screenshot](/website-screenshot) | `GET` | `/v2/website/screenshot` | 2 |
| [Website Technology](/website-technology) | `GET` | `/v2/website/technology` | 1 |
| [Website WebUnlocker](/website-webunlocker) | `GET` | `/v2/website/webunlocker` | 3 |
| [Weibo Post Info](/weibo-post-info) | `GET` | `/v2/weibo/post/info` | 1 |
| [Weibo Search](/weibo-search) | `GET` | `/v2/weibo/search` | 1 |
| [Wellfound Company Info](/wellfound-company-info) | `GET` | `/v2/wellfound/company/info` | 1 |
| [X Post Info (twitter)](/x-post-info) | `GET` | `/v2/x/post/info` | 2 |
| [X Profile Info (twitter)](/x-profile-info) | `GET` | `/v2/x/profile/info` | 2 |
| [Yellowpages Business](/yellowpages-business) | `GET` | `/v2/yellowpages/business` | 2 |
| [Yellowpages Search](/yellowpages-search) | `GET` | `/v2/yellowpages/search` | 2 |
| [Yelp Business](/yelp-business) | `GET` | `/v2/yelp/business` | 2 |
| [Yelp Reviews](/yelp-reviews) | `GET` | `/v2/yelp/reviews` | 2 |
| [Yelp Search](/yelp-search) | `GET` | `/v2/yelp/search` | 2 |
| [Zillow Property](/zillow-property) | `GET` | `/v2/zillow/property` | 3 |
| [Zillow Search](/zillow-search) | `GET` | `/v2/zillow/search` | 3 |
| [Zoopla Property](/zoopla-property) | `GET` | `/v2/zoopla/property` | 2 |
| [Zoopla Search](/zoopla-search) | `GET` | `/v2/zoopla/search` | 2 |
| [Zooplus Product](/zooplus-product) | `GET` | `/v2/zooplus/product` | 1 |
| [Zooplus Search](/zooplus-search) | `GET` | `/v2/zooplus/search` | 1 |
# Error Handling (/error-handling)
Every request to the Piloterr API returns a standard HTTP status code. This guide covers what each code means, when credits are consumed, and how to handle failures in your integration.
## Response format [#response-format]
Successful responses return a JSON object with the data for that endpoint. Error responses follow a consistent structure:
```json
{
"error": "Human-readable description of what went wrong"
}
```
Always check the HTTP status code **before** reading the body.
***
## Status codes [#status-codes]
### `200` Success [#200-success]
**Billed.** The request completed successfully. Credits are deducted from your balance.
```typescript
const res = await fetch(url, { headers: { "x-api-key": API_KEY } })
if (res.status === 200) {
const data = await res.json()
// process data
}
```
### `201` Async Job Completed [#201-async-job-completed]
**Billed.** An asynchronous job finished successfully. You never receive `201` as an HTTP response — it is the final status recorded in your request logs for an async call, and this is the moment the job is billed. Your balance is checked before the job is queued, so a job never starts without the credits to pay for it.
### `202` Accepted (Async) [#202-accepted-async]
**Not billed.** The async request was accepted and queued for processing. Poll `GET /jobs/{job_id}` for the result — polling is always free. You are only billed once the job completes successfully (recorded as `201` in your request logs); failed jobs are never billed.
### `400` Bad Request [#400-bad-request]
**Not billed.** Your request contains invalid or missing parameters.
**Common causes:**
* A required query parameter is missing
* A parameter value has the wrong type or format
* The request body is malformed
**What to do:** Re-read the endpoint documentation and verify every required parameter. Log the full request URL to spot typos.
```typescript
if (res.status === 400) {
const { error } = await res.json()
console.error("Bad request:", error)
// do not retry, fix the parameters first
}
```
### `401` Unauthorized [#401-unauthorized]
**Not billed.** Returned for authentication issues **and** rate limiting. Check the error message to distinguish between them.
Your `x-api-key` header is missing or contains an unrecognized value.
**Fix:** Copy your key from your [dashboard → API Keys](https://app.piloterr.com) and make sure the `x-api-key` header is present on every request.
The key exists but has been deactivated.
**Fix:** Go to your [dashboard → API Keys](https://app.piloterr.com) and activate the key, or generate a new one.
The key has passed its expiry date.
**Fix:** Generate a new key or extend the expiry from your dashboard.
You have sent too many requests in a short window. Plan limits look like `Rate limit exceeded: You've exceeded the … rate limit on your subscription`. Per-key quotas look like `Rate limit exceeded for the API key: quota daily` (or `total` / `weekly` / `monthly`).
**Fix:** Back off and retry after a delay. If you hit this regularly, upgrade your plan or raise the key's quotas. These limits return `401`, not `429`.
```typescript
if (res.status === 401) {
const { error } = await res.json()
if (error.toLowerCase().includes("rate limit")) {
await new Promise(r => setTimeout(r, 2000))
// retry
}
}
```
### `402` Payment required [#402-payment-required]
**Not billed.** The request was not executed. The `error` string tells you why:
* `Payment required` — API quota and pay-as-you-go credits cannot cover the call
* `You have open invoices: …` — an unpaid invoice is blocking the account
* `Insufficient credits: X required, Y available` — a per-row endpoint would return more rows than you can afford
**What to do:**
1. Purchase a top-up or upgrade your plan from your [dashboard](https://app.piloterr.com), or settle open invoices under **Settings → Invoices**.
2. Or enable [Auto Top-Up](/auto-top-up) to prevent balance exhaustion from happening again.
Auto Top-Up only triggers when your balance drops below the configured threshold. It never runs on a fixed schedule or at a specific time. A built-in **72-hour cooldown** also prevents multiple charges in a short period: once a top-up fires, it cannot trigger again for 72 hours regardless of how your balance moves. If a charge fails, Auto Top-Up is automatically disabled to protect your account.
Do not retry a `402` response immediately. The call will fail again until you add credits. Set up an alert so your team is notified when this happens.
```typescript
if (res.status === 402) {
// alert your team or trigger an auto top-up flow
throw new Error("Insufficient credits. Top up at https://app.piloterr.com")
}
```
### `403` Forbidden [#403-forbidden]
**Not billed.** The request was refused before it ran. The usual causes are an endpoint that does not exist on your plan, a deactivated account, or an IP temporarily blocked after repeated failed authentications.
On [Stats](/stats), HTTP 403 means **API Stats is not enabled for this account**. The `error` field says to contact support to activate it.
**What to do:** Check the endpoint path against the reference, then confirm your account is active in the dashboard. For API Stats, open a support ticket and ask us to enable it. Contact support if you believe this is a mistake.
### `404` Not Found [#404-not-found]
**May be billed** (endpoint-specific). The resource was not found. Check the individual endpoint documentation to confirm whether this response is charged.
```typescript
if (res.status === 404) {
// handle "no result" gracefully, not as a fatal error
return null
}
```
### `500` Internal Server Error [#500-internal-server-error]
**Not billed.** An unexpected error occurred on our side.
**What to do:** Retry with exponential backoff. If the issue persists, contact support.
```typescript
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(url, options)
if (res.status !== 500 || attempt === maxRetries) return res
const delay = 500 * 2 ** attempt
console.warn(`500 error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`)
await new Promise(r => setTimeout(r, delay))
}
}
```
***
## Complete error handler [#complete-error-handler]
A single function that covers every status code:
```typescript
interface ApiResult {
data: T | null
error: string | null
status: number
billed: boolean
}
async function apiCall(url: string, apiKey: string): Promise> {
const res = await fetch(url, {
headers: { "x-api-key": apiKey },
})
// 200 and 201 are always billed; 404 billing is endpoint-specific
const billed = res.status === 200 || res.status === 201
if (res.status === 200) {
return { data: await res.json() as T, error: null, status: 200, billed }
}
const body = await res.json().catch(() => ({ error: "Unknown error" }))
const error = body?.error ?? "Unknown error"
switch (res.status) {
case 400:
console.error("[400] Bad request, fix your parameters:", error)
break
case 401:
if (error.toLowerCase().includes("rate limit")) {
console.warn("[401] Rate limit, back off and retry")
} else {
console.error("[401] Auth error, check your API key:", error)
}
break
case 402:
console.error("[402] No credits remaining. Top up at https://app.piloterr.com")
break
case 403:
console.error("[403] Forbidden, check your key permissions")
break
case 404:
console.warn("[404] Not found, no result for this request")
break
case 500:
console.error("[500] Server error, retry with backoff")
break
default:
console.error(`[${res.status}] Unexpected status:`, error)
}
return { data: null, error, status: res.status, billed }
}
```
***
## Retry strategy [#retry-strategy]
| Status | Retry? | Strategy |
| ------------------ | ------ | ----------------------------------------------------------- |
| `400` | No | Fix the request first |
| `401` (rate limit) | Yes | Wait 2–5 s, then retry |
| `401` (auth) | No | Fix the API key |
| `402` | No | Add credits or settle invoices first |
| `403` | No | Check permissions, or contact support to activate API Stats |
| `404` | No | Handle as empty result |
| `500` | Yes | Exponential backoff (max 3 attempts) |
***
## Billing summary [#billing-summary]
| Status | Billed |
| -------------------------------------------- | --------------------------------------------------------------- |
| `200` (sync call) | Yes |
| `201` (async job completed, in request logs) | Yes |
| `202` (async accepted) | No — billed when the job completes (`201` in your request logs) |
| `404` | Endpoint-specific (check the endpoint docs) |
| All other codes | No |
See the [Credits](/credits) page for a full cost breakdown per endpoint. For broader integration advice, see [Best Practices](/best-practices).
# Global Parameters (/global-parameters)
These parameters can be used with any API endpoint.
| Parameter | Type | Default | Description |
| ----------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `x_api_key` | `string` | - | Authentication key required for all requests. Pass it in the `x-api-key` header or as a query string. |
| `soft_fail` | `boolean` | `false` | When `true`, the API returns HTTP 200 even when an error occurs. Errors are still tracked and billed normally. |
| `is_async` | `boolean` | `false` | When `true`, the request is processed asynchronously. The response contains a `job_id` that you poll on `/jobs/{job_id}`. |
| `fields` | `string` | - | Comma-separated list of fields to include in the response. Supports nested fields with dot notation. |
## Fields filtering [#fields-filtering]
Use the `fields` parameter to keep only the data you actually need in the response. This makes responses smaller, faster to download and easier to parse on your side. It is especially useful when your platform enforces a payload size limit (Make.com, Zapier, low-memory Lambdas, etc.).
### Syntax [#syntax]
Pass `fields` as a comma-separated list of field names. Use a dot (`.`) to drill into nested objects.
* Top-level fields are listed by name: `fields=name,founded`.
* Nested fields use dot notation: `fields=address.city,address.country`.
* Whitespace around commas is ignored: `fields=name, founded` works the same.
* Unknown field names are silently ignored, no error is returned.
### Selecting top-level fields [#selecting-top-level-fields]
Keep only the top-level keys you care about. The original structure is preserved for the kept fields.
```bash
curl -X GET "https://api.piloterr.com/{endpoint}?param=value&fields=name,founded,tagline" \
-H "x-api-key: YOUR_API_KEY"
```
**Response:**
```json
{
"name": "Example",
"founded": "2020",
"tagline": "Just an example"
}
```
### Selecting nested fields [#selecting-nested-fields]
Use dot notation to reach into nested objects. Only the requested subfields are returned, the rest of the parent object is dropped. Dot notation also works for objects inside arrays: `?fields=team.name` keeps the `name` of every item in `team`.
```bash
curl -X GET "https://api.piloterr.com/{endpoint}?param=value&fields=name,location.city,location.country" \
-H "x-api-key: YOUR_API_KEY"
```
**Response:**
```json
{
"name": "Example",
"location": {
"city": "Paris",
"country": "France"
}
}
```
### Good to know [#good-to-know]
* Filtering happens **after** the data is fetched: it does **not** reduce the credit cost of the call. It does reduce response size, parsing time and bandwidth.
* Top-level keys not listed in `fields` are removed from the response. Listing nested fields automatically keeps their parent object.
* Combine `fields` freely with other parameters such as `soft_fail` or `is_async`.
## Soft fail mode [#soft-fail-mode]
Handle errors gracefully by always receiving a 200 status code.
Add `soft_fail=true` to any request to receive HTTP 200 even when an error occurs.
Errors are still tracked in your dashboard and billed according to standard rules.
### Use cases [#use-cases]
* Systems that treat non-200 codes as fatal (Make.com, Zapier)
* Custom error handling in your application
* Consistent error handling across integrations
**Note for Make.com users:** by default, Make.com scenarios stop with an error when they receive a `404` or `500` HTTP status code. Using `soft_fail=true` ensures your scenario keeps running even when the API returns an error, so you can handle it gracefully in subsequent modules.
### Example [#example]
```bash
curl -X GET "https://api.piloterr.com/{endpoint}?param=invalid&soft_fail=true" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"error": "Not Found"
}
```
## Async mode [#async-mode]
For long-running requests, use async mode to avoid timeouts.
Add `is_async=true` to receive a `job_id` immediately.
### How it works [#how-it-works]
1. Send the request with `is_async=true`
2. Receive a `job_id`
3. Poll `/jobs/{job_id}` to retrieve the result
```bash
# Step 1: start the job
curl -X GET "https://api.piloterr.com/{endpoint}?param=value&is_async=true" \
-H "x-api-key: YOUR_API_KEY"
# Response: { "job_id": "abc123..." }
# Step 2: retrieve the result
curl -X GET "https://api.piloterr.com/jobs/abc123..." \
-H "x-api-key: YOUR_API_KEY"
```
See [Status Codes](/status-codes) for response codes, [Best Practices](/best-practices) for integration guidance, and [Error Handling](/error-handling) for retry strategies.
# Glossary (/glossary)
A reference for the terms you will encounter in the documentation, dashboard, and API responses.
***
## Account [#account]
See [Tenant](#tenant).
***
## API key [#api-key]
A secret string used to authenticate API requests. Each key belongs to a tenant and has an alias, category, optional quotas, and an optional expiry date. Passed via the `x-api-key` header or `x_api_key` query parameter.
See [API Keys](/api-keys) for full details.
***
## API Stats [#api-stats]
A free endpoint that returns platform infrastructure health for an engine or path over the last hour. It is not your account or tenant usage. Access is enabled per account. HTTP 403 means it is not active on your account: contact support to activate it.
See [Stats](/stats).
***
## API top-up [#api-top-up]
A one-time purchase that adds consumable API quota. Top-ups do not reset each billing period. API calls consume subscription quota first, then top-up capacity until exhausted.
Purchase top-ups from your dashboard billing section or pricing page.
***
## Alias [#alias]
The human-readable name of an API key, unique within a tenant. Used to identify keys in the dashboard and analytics.
***
## Auto Top-Up [#auto-top-up]
A feature that automatically purchases a one-time pack when your remaining balance (API quota + extra credits) drops below a configured threshold. Protected by a 72-hour cooldown between purchases. Requires an active paid subscription.
See [Auto Top-Up](/auto-top-up) for full details.
***
## Billing period [#billing-period]
The active subscription window used to measure quota consumption. Determined by the `currentPeriodStart` and `currentPeriodEnd` fields from Stripe. If no active subscription is found, the billing period defaults to the current calendar month.
***
## Category [#category]
A label on an API key (`production`, `development`, `staging`, or custom). Used for filtering and organization in the dashboard. Does not change how the API processes the request.
***
## Credit [#credit]
The unit of API consumption. Each billable request deducts a number of credits determined by the endpoint's cost. Credits are stored as ledger entries: a negative amount means credits were granted, a positive amount means they were consumed.
See [Credits](/credits) for the cost per endpoint.
***
## Extra credits [#extra-credits]
Credits stored in a separate ledger, distinct from API quota. Used only after your combined API quota (subscription + top-ups) is fully exhausted. They do not reset at the end of a billing period. Sources include onboarding rewards and manual extra-credit purchases.
***
## Per-key quota [#per-key-quota]
An optional request limit set on a specific API key. Enforced on rolling time windows: all-time total, last 24 hours, last 7 days, and last 30 days. Independent of the subscription quota.
***
## Plan [#plan]
A subscription product (e.g. "Starter", "Pro") that defines a set of features: API call quota, rate limits, access to extra credits, and more. A tenant can hold multiple active plan products simultaneously.
***
## Quota [#quota]
Your combined API call budget from subscription allowance and any remaining one-time top-ups. Weighted by per-endpoint credit cost. Subscription portion resets each billing period; top-ups are consumed once. When API quota is exhausted, extra credits are used if available; otherwise requests return `402`.
***
## Rate limit [#rate-limit]
There are two separate rate-limiting systems. **Plan limits** (per second / per minute) apply to all keys on an account and are set by the subscription plan. **Per-key quotas** (total / daily / weekly / monthly rolling windows) are optional hard caps configured per key. Both return `401` with a `Rate limit exceeded` message — the message tells you which one you hit. See [API Keys](/api-keys) for details.
***
## Subscription [#subscription]
A recurring billing arrangement linking a tenant to one or more plans. Managed through Stripe. Determines the billing period, quota, and features available to the tenant.
***
## Tenant [#tenant]
The organizational unit in the platform, equivalent to an account or workspace. All API keys, usage, credits, and subscriptions are scoped to a tenant. Users can belong to multiple tenants. See [Account](#account).
***
## Threshold [#threshold]
The percentage of remaining balance (API quota + extra credits) at which Auto Top-Up activates. Configurable between 10% and 30%.
# Introduction (/introduction)
Get started with the Piloterr API in minutes. Make HTTP requests to our endpoints and receive structured JSON responses.
## Quick Start [#quick-start]
### Get your API key [#get-your-api-key]
Sign up on your [dashboard](https://app.piloterr.com) and copy your API key from the **Settings** page.
### Make your first request [#make-your-first-request]
Include your key in the `x-api-key` header and call any endpoint:
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: YOUR_API_KEY"
```
### Handle the response [#handle-the-response]
Every successful request returns a JSON object with structured data. See individual endpoint pages for response schemas and examples.
## Authentication [#authentication]
All API requests must include an `x-api-key` header. You can find your key in your [dashboard → API Keys](https://app.piloterr.com/).
```bash
curl -H "x-api-key: xxxx" https://api.piloterr.com/v2/usage
```
Keep your API key secret. Do not expose it in client-side code or public repositories.
## Status Codes [#status-codes]
**Synchronous calls** are billed on `200`. **Asynchronous calls** (`is_async=true`) return `202` (not billed) and are billed when the job completes successfully — recorded as `201` in your request logs. Polling `GET /jobs/{job_id}` is always free. Failed jobs (`500`) are never billed. `404` billing varies by endpoint (check each endpoint page). All other 4xx/5xx responses are free.
| Code | Billed | Status | Action |
| ----- | ------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Yes | Successful API Call (sync) | No action required. |
| `201` | Yes | Async Job Completed | Appears in your request logs when an async job finishes successfully — this is when the job is billed. Not returned as an HTTP response. |
| `202` | No | Accepted (Async) | The job was accepted and queued. Poll `GET /jobs/{job_id}` for the result — polling is always free. |
| `400` | No | Bad Request | Verify your parameters and their types. Check the documentation for more information. |
| `401` | No | Invalid API Key | Check your API key (`x-api-key` header or `x_api_key` query string). |
| `401` | No | Inactive API Key | Activate your API key in the API Keys section of your account settings. |
| `401` | No | Expired API Key | Update your API key or generate a new one. |
| `401` | No | Rate limit exceeded | Back off and retry. Upgrade your plan or raise per-key quotas if this happens often. |
| `402` | No | Payment required | Top up credits, upgrade your plan, enable Auto Top-Up, or settle any open invoices. |
| `403` | No | Forbidden | Verify your permissions. For API Stats, HTTP 403 means the endpoint is not enabled on your account: contact support to activate it. |
| `404` | Varies | Not Found | Billing is endpoint-specific. Check the individual endpoint documentation. |
| `413` | No | Payload Too Large | The uploaded file or request body exceeds the size limit. Check the API documentation for maximum sizes. |
| `500` | No | Internal Error | Retry the action or contact our support team. |
See the [Credits](/credits) page for a full breakdown per endpoint, [Stats](/stats) for infrastructure health, [API Keys](/api-keys) for key management, and [Best Practices](/best-practices) and [Error Handling](/error-handling) for integration guidance. New to the platform? The [Glossary](/glossary) defines all key terms.
## Endpoints [#endpoints]
Browse every endpoint in the **API reference**, or open one directly:
# Model Context Protocol (/mcp)
The **Model Context Protocol (MCP)** lets AI assistants call **Piloterr** as **tools**: same operations as the REST API, with your **API key**. You can document setup like many teams do: a short README, a sample **`mcp.json`**, and copy-paste steps for your users.
## What you need [#what-you-need]
* A **Piloterr** account ([register](https://app.piloterr.com/register) if needed) and access to the [dashboard](https://app.piloterr.com).
* An **API key** with the **`x-api-key`** header (see [API Keys](/api-keys)).
* **MCP endpoint:** **`https://mcp.piloterr.com/`**
## How it works [#how-it-works]
The server uses **Streamable HTTP** at **`https://mcp.piloterr.com/`**. It is not a normal web page. Each **tool** maps to an API operation; billing and behaviour match the REST API. Use the rest of this documentation for endpoints and parameters.
## Claude Code [#claude-code]
Official docs: [Model Context Protocol (MCP)](https://code.claude.com/docs/en/mcp). Example: **`claude mcp add --transport http piloterr https://mcp.piloterr.com/`** with **`--header`** **`x-api-key: YOUR_API_KEY`**. Use the same URL as in the Cursor section below.
## Cursor [#cursor]
See the [Cursor MCP documentation](https://cursor.com/docs/mcp). In **Settings → MCP**, add an **HTTP** server, or use a project **`mcp.json`** with a **`url`** (ending in `/`) and **`headers`** for your API key.
Use the **MCP endpoint** for **Piloterr** below. Never commit secrets. Prefer environment-specific config or a placeholder like `YOUR_API_KEY`.
```json
{
"mcpServers": {
"piloterr": {
"url": "https://mcp.piloterr.com/",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}
```
## Gemini CLI [#gemini-cli]
See [MCP server](https://geminicli.com/docs/tools/mcp-server/) in the Gemini CLI documentation. Point the client at **`https://mcp.piloterr.com/`** and pass your API key (for example via **`x-api-key`**) as the tool docs describe.
## OpenAI Developer Mode [#openai-developer-mode]
See [Developer mode](https://developers.openai.com/api/docs/guides/developer-mode) in the OpenAI API documentation. Wire MCP tools to **`https://mcp.piloterr.com/`** and your **API key** following that guide.
## See also [#see-also]
* [API Keys](/api-keys): create and manage keys
* [Best practices](/best-practices): secure integration
# Stats (/stats)
The Stats endpoint returns a single infrastructure health snapshot for an API engine or a specific path. Metrics are aggregated across the whole platform over a one-hour window. They are not your account or tenant usage. There is no hourly time series. Use [Usage](/usage) for your account quota.
This endpoint is free. It never consumes any credits.
API Stats is enabled per account. HTTP 403 means it is not active on your account. Contact support to activate it.
These are infrastructure stats for the platform, not stats for your account or tenant. Every authenticated caller sees the same health snapshot. For remaining credits and rate limits, use [Usage](/usage).
## Query parameters [#query-parameters]
Provide at least one of `engine` or `path`.
* `engine` (string): API family to check, for example `linkedin` or `google`.
* `path` (string): specific endpoint to check, for example `/v1/linkedin/profile-info`. If you also pass `engine`, use an endpoint from that API.
## Request [#request]
```bash
curl "https://api.piloterr.com/v2/stats?engine=linkedin" \
-H "x-api-key: YOUR_API_KEY"
```
```bash
curl "https://api.piloterr.com/v2/stats?path=/v1/linkedin/profile-info" \
-H "x-api-key: YOUR_API_KEY"
```
## Health values [#health-values]
* `operational`: success rate at or above 99%.
* `degraded`: success rate from 90% up to 99%.
* `down`: success rate below 90%.
## Response Fields [#response-fields]
| Field | Type | Description |
| ---------------------- | ---------------- | -------------------------------------------------------------------------------------------------- |
| `engine` | `string` | Engine slug derived from the catalog (for example `linkedin`) |
| `path` | `string \| null` | Normalized path filter, or `null` when you query by engine only |
| `health` | `string` | `operational`, `degraded`, or `down` for the last hour |
| `success_rate` | `number` | Success rate over the last hour. Only HTTP 5xx counts as failure. Low-volume windows are smoothed. |
| `avg_response_time_ms` | `integer` | Average response time in milliseconds. Durations above 60 seconds are excluded. |
| `window` | `string` | Aggregation window. Always `1h`. |
The response does not include request volume, raw error counts, or any account-level or tenant-level data.
## Example Response [#example-response]
```json
{
"engine": "linkedin",
"path": "/v1/linkedin/profile-info",
"health": "operational",
"success_rate": 99.412,
"avg_response_time_ms": 840,
"window": "1h"
}
```
## Errors [#errors]
| Code | Description |
| ----- | -------------------------------------------------------------------------------- |
| `400` | Missing `engine` and `path`, invalid value, or path that does not match `engine` |
| `401` | Missing or invalid API key |
| `403` | API Stats is not enabled for this account. Contact support to activate it. |
| `404` | Unknown engine (not in the API catalog) |
| `503` | Status data is temporarily unavailable |
See also: [Usage](/usage) for your account quota (tenant-scoped), [Status Codes](/status-codes) for HTTP billing rules, and [Error Handling](/error-handling) for integration errors.
# Status Codes (/status-codes)
**Synchronous calls** are billed on `200`. **Asynchronous calls** (`is_async=true`) return `202` (not billed) and are billed when the job completes successfully — recorded as `201` in your request logs. Polling `GET /jobs/{job_id}` is always free. Failed jobs (`500`) are never billed. `404` billing varies by endpoint (check each endpoint page). All other 4xx/5xx responses are free.
| Code | Billed | Status | Action |
| ----- | ------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Yes | Successful API Call (sync) | No action required. |
| `201` | Yes | Async Job Completed | Appears in your request logs when an async job finishes successfully — this is when the job is billed. Not returned as an HTTP response. |
| `202` | No | Accepted (Async) | The job was accepted and queued. Poll `GET /jobs/{job_id}` for the result — polling is always free. |
| `400` | No | Bad Request | Verify your parameters and their types. Check the documentation for more information. |
| `401` | No | Invalid API Key | Check your API key (`x-api-key` header or `x_api_key` query string). |
| `401` | No | Inactive API Key | Activate your API key in the API Keys section of your account settings. |
| `401` | No | Expired API Key | Update your API key or generate a new one. |
| `401` | No | Rate limit exceeded | Back off and retry. Upgrade your plan or raise per-key quotas if this happens often. |
| `402` | No | Payment required | Top up credits, upgrade your plan, enable Auto Top-Up, or settle any open invoices. |
| `403` | No | Forbidden | Verify your permissions. For API Stats, HTTP 403 means the endpoint is not enabled on your account: contact support to activate it. |
| `404` | Varies | Not Found | Billing is endpoint-specific. Check the individual endpoint documentation. |
| `413` | No | Payload Too Large | The uploaded file or request body exceeds the size limit. Check the API documentation for maximum sizes. |
| `500` | No | Internal Error | Retry the action or contact our support team. |
# Timeouts (/timeout)
Most requests respond in a few seconds, but some endpoints can take longer to complete. This page explains our server-side limit and how to make sure your client does not give up before we respond.
## Server-side limit: 60 seconds [#server-side-limit-60-seconds]
All real-time API calls have a hard timeout of **60 seconds** on our side. Typical responses are returned in **2-3 seconds**, occasionally up to **30 seconds**, and the worst case before we abort is **60 seconds**.
If you need to run requests that may take longer than 60 seconds, switch to **async mode** with `is_async=true`. See [Global Parameters](/global-parameters#async-mode).
## Why your request seems to time out earlier [#why-your-request-seems-to-time-out-earlier]
If you observe consistent timeouts at exactly **15s**, **30s**, or any value below 60 seconds, the connection is almost always being closed by **your own infrastructure**, not by our API. Your runtime aborts the HTTP call before we have time to respond.
**Common signs:**
* Errors fire at the exact same duration on every retry (often 10s, 15s, 25s, 30s).
* Our dashboard shows the request as `200` (success) while your client reports a timeout.
* Smaller / cached requests succeed, but heavier ones always fail.
## Platform timeouts to check [#platform-timeouts-to-check]
Below are the default HTTP / function timeouts for the most common platforms. Increase them to at least **60 seconds** to match our server-side limit.
Vercel Serverless and Edge Functions default to **15s** on the Hobby plan (up to **60s**), and **15s** on Pro (up to **300s**, configurable per function).
Set `maxDuration` in your route or function:
```typescript
// app/api/my-route/route.ts (Next.js App Router on Vercel)
export const maxDuration = 60
export async function POST(req: Request) {
// ... call our API
}
```
Or in `vercel.json`:
```json
{
"functions": {
"app/api/**/*.ts": { "maxDuration": 60 }
}
}
```
Reference: [Vercel function duration](https://vercel.com/docs/functions/configuring-functions/duration).
AWS Lambda functions default to **3 seconds** and can be configured up to **15 minutes**.
```bash
# AWS CLI
aws lambda update-function-configuration \
--function-name my-function \
--timeout 60
```
If you call Lambda through API Gateway, also raise the integration timeout (REST APIs default to **29s**, HTTP APIs to **30s**).
Netlify Functions default to **10 seconds** (synchronous) and can be raised to **26 seconds**. For longer requests, use **Background Functions** (up to 15 minutes) or our [async mode](/global-parameters#async-mode).
```toml
# netlify.toml
[functions]
timeout = 26
```
Cloudflare Workers have a **CPU time** limit (10ms / 50ms free, up to 30s paid) but **wall-clock time** is essentially unlimited as long as the connection stays open. Most timeouts come from the upstream `fetch` call.
```typescript
const res = await fetch("https://api.example.com/...", {
signal: AbortSignal.timeout(60_000),
})
```
Make.com (formerly Integromat) has a default HTTP module timeout of **40 seconds**, configurable per call up to **300 seconds**.
In the HTTP module → **Show advanced settings** → set **Timeout** to `60` (or higher).
If your scenario stops on transient HTTP errors, also check our [Soft Fail mode](/global-parameters#soft-fail-mode).
Most HTTP clients have a default request timeout. Make sure you set it to at least **60 seconds**:
```typescript
// Node.js fetch with AbortController
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), 60_000)
try {
const res = await fetch(url, { signal: controller.signal })
} finally {
clearTimeout(timer)
}
```
```python
# Python requests
import requests
requests.get(url, timeout=60)
```
```bash
# curl
curl --max-time 60 ...
```
## Still seeing timeouts? [#still-seeing-timeouts]
If your client is configured for at least 60 seconds, async mode does not fit your use case, and you still see timeouts, please open a support ticket from your [dashboard](https://app.piloterr.com/) with: the endpoint, a sample `request_id`, the timestamp, and the timeout value you observe. We will investigate.
See [Global Parameters](/global-parameters) for `is_async` and `soft_fail`, [Status Codes](/status-codes) for response codes, and [Best Practices](/best-practices) for retry guidance.
# Usage (/usage)
The Usage endpoint returns your remaining API quota, extra credits, rate limits, and API key details at any time. It does not consume any credits.
Note: the `subscription` object reports your **combined API quota** (subscription allowance + remaining top-ups), not the subscription portion alone. Use the dashboard for a subscription vs top-up split.
This endpoint is free. It never consumes any credits.
## Request [#request]
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: YOUR_API_KEY"
```
## Response Fields [#response-fields]
| Field | Type | Description |
| --------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
| `remaining` | `integer` | Total credits available now (API quota + extra credits) |
| `total_used` | `integer` | Total credits consumed so far (API quota + extra credits; top-up usage persists across billing periods) |
| `renewal_date` | `string` | Date when the subscription renews (ISO 8601) |
| `period.start` | `string` | Start of the current billing period |
| `period.end` | `string` | End of the current billing period |
| `subscription.used` | `integer` | API calls used against your combined API quota |
| `subscription.total` | `integer` | Combined API quota cap (subscription + top-ups) |
| `subscription.remaining` | `integer` | Remaining combined API quota (subscription remainder + unused top-ups) |
| `subscription.percent_used` | `number` | Percentage of API quota consumed |
| `credits.given` | `integer` | Extra credits granted to your account (separate ledger) |
| `credits.consumed` | `integer` | Extra credits consumed |
| `credits.remaining` | `integer` | Extra credits remaining |
| `rate_limit.per_minute` | `integer \| null` | Requests per minute limit (`null` = unlimited) |
| `rate_limit.per_second` | `integer \| null` | Requests per second limit (`null` = unlimited) |
| `account.name` | `string` | Name of the account |
| `account.slug` | `string` | Account identifier slug |
| `api_key.alias` | `string` | Friendly name of the API key |
| `api_key.active` | `boolean` | Whether the key is currently active |
| `api_key.category` | `string` | Key category (e.g. `development`, `production`) |
| `api_key.expires` | `string \| null` | Expiry date of the key (`null` = never expires) |
| `api_key.quotas` | `object` | Custom per-key quotas: `total`, `daily`, `weekly`, `monthly` |
## Example Response [#example-response]
```json
{
"remaining": 450,
"total_used": 1550,
"renewal_date": "2026-04-25",
"period": {
"start": "2026-03-25",
"end": "2026-04-25"
},
"subscription": {
"used": 1550,
"total": 2000,
"remaining": 450,
"percent_used": 77
},
"credits": {
"given": 0,
"consumed": 0,
"remaining": 0,
"percent_used": 0
},
"rate_limit": {
"per_minute": null,
"per_second": null
},
"account": {
"name": "Acme Corp",
"slug": "acme-corp"
},
"api_key": {
"alias": "production",
"active": true,
"category": "production",
"expires": null,
"quotas": {
"total": null,
"daily": null,
"weekly": null,
"monthly": null
}
}
}
```
See also: [Stats](/stats) for platform infrastructure health over the last hour. That endpoint is not your account usage.
# Help center (/)
Welcome to the Piloterr help center. This guide covers everything you do in the dashboard: creating an account, issuing API keys, tracking credits, reading request logs, and inviting your team. Looking for endpoint parameters instead? Head to the **API reference**.
## Start here [#start-here]
* **New to Piloterr?** Follow the [quick start](/getting-started/quick-start) to go from signup to your first API response.
* **Ready to integrate?** Create a key in [API keys](/keys/create-and-manage), then read the [Developer guide](/introduction).
* **Watching your spend?** [Credits](/billing/credits) explains how balances work and when calls are billed.
## How the documentation is organised [#how-the-documentation-is-organised]
| Section | What you will find |
| ------------------- | ------------------------------------------------------------------------ |
| **User guide** | Everything you do in the dashboard, with an illustration of each screen. |
| **Developer guide** | Authentication, global parameters, error handling, and playbooks. |
| **API reference** | OpenAPI pages for every endpoint: parameters, examples, and responses. |
## A typical path [#a-typical-path]
* Create an account and land on the **Library**.
* Issue an **API key**, store the secret, and call `/v2/usage` to verify authentication.
* Open an endpoint in the Library, try it in the playground, then wire the same call into your backend.
* Watch spend in **Analytics**, debug individual calls in **API Logs**, and top up from **Settings → Subscription** when the balance drops.
Every page has a **Copy markdown** button at the top. Paste it into an AI assistant to get help tailored to your integration.
## Related [#related]
# Action Product (/action-product)
**GET** `https://api.piloterr.com/v2/action/product`
Action Product API: fetch full product page data from a product ID or URL on Action.com. Returns pricing, specs, images, breadcrumbs and availability via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Action product ID or full product URL. **Examples:** ``` 3216159 ``` ``` https://www.action.com/fr-fr/p/3216159/etagere/ ``` ``` https://www.action.com/pt-pt/p/2535685/cabides-de-veludo/ ``` |
| `region` | query | `string` | No | Locale (`fr-fr`, `de-de`, `pt-pt`, …) or country code (`fr`, `de`, `pt`). Default: `fr-fr`. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"locale": {
"type": "string"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"availability": {
"type": "string"
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Action Search (/action-search)
**GET** `https://api.piloterr.com/v2/action/search`
Action Search API: search product listings by keyword or URL on Action.com across European locales. Returns prices, images, locale, currency and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Action.com search URL. **Examples:** ``` meuble ``` ``` https://www.action.com/fr-fr/search/?q=meuble ``` ``` https://www.action.com/pt-pt/search/?q=cabide ``` Use **Action Product** on any result `url` or `product_id` for full PDP data. |
| `region` | query | `string` | No | Locale (`fr-fr`, `de-de`, `pt-pt`, …) or country code (`fr`, `de`, `pt`). Default: `fr-fr`. For multilingual countries use full locale (`fr-be`, `nl-be`, `fr-ch`, `de-ch`, `it-ch`). |
| `page` | query | `number` | No | 1-based page number (default: 1). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"locale": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Aldi Product (/aldi-product)
**GET** `https://api.piloterr.com/v2/aldi/product`
Aldi Product API: fetch full product page data from an Aldi URL or path slug. Returns pricing, images, description, category and cooling flags.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Aldi product page URL or site path slug. **Examples:** ``` fiches-produits/filets-danchois-marines-6806.html ``` ``` https://www.aldi.fr/fiches-produits/filets-danchois-marines-6806.html ``` Numeric product id alone is not supported: use URL or slug path. |
| `region` | query | `string` | No | Target country when `query` is a path only (default: `fr`). Ignored for full product URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"is_cooling": {
"type": "boolean"
},
"product_id": {
"type": "string"
},
"sales_unit": {
"type": "string"
},
"description": {
"type": "string"
},
"product_slug": {
"type": "string"
},
"short_description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Aldi Search (/aldi-search)
**GET** `https://api.piloterr.com/v2/aldi/search`
Aldi Search API: list products from an Aldi category page URL or path across FR, DE, NL and Canarias shops. Returns prices, thumbnails and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Aldi category page URL or site path. **Examples:** ``` produits/viande-poisson/poisson-crustace.html ``` ``` https://www.aldi.fr/produits/viande-poisson/poisson-crustace.html ``` Use **Aldi Product** on any result `url` for full PDP data. |
| `region` | query | `string` | No | Target country when `query` is a path only (default: `fr`). **Examples:** `de`, `nl`, `es-can` Ignored when `query` is a full Aldi URL. |
| `page` | query | `number` | No | 1-based page number override (default: read from URL or `1`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"sales_unit": {
"type": "string"
},
"product_slug": {
"type": "string"
},
"short_description": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Alibaba Product (/alibaba-product)
**GET** `https://api.piloterr.com/v2/alibaba/product`
Alibaba Product API. Fetch a full B2B product page by URL or product ID with optional locale subdomain. Browser rendering returns tiered pricing, seller profile and lead times.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Alibaba product URL or numeric product ID. **Examples (URL):** https://www.alibaba.com/product-detail/Custom-Neon-Light-Acrylic-Neon-Sign_1601175379813.html **Examples (ID):** 1601175379813 Bare IDs resolve on `www.alibaba.com`. Use `subdomain=portuguese` for a localized PDP. Discover `listing_url` values with [Alibaba Search](https://www.piloterr.com/library/alibaba-search). |
| `subdomain` | query | `string` | No | Locale subdomain when `query` is a bare product ID, or to override the host on a product URL. **Examples:** ``` portuguese ``` ``` french.alibaba.com ``` Defaults to `www` when omitted on numeric IDs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the rendered HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"unit": {
"type": "string"
},
"display": {
"type": "string"
},
"quantity_prices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"display": {
"type": "string"
},
"price_usd": {
"type": "number"
},
"max_quantity": {
"type": "integer"
},
"min_quantity": {
"type": "integer"
}
}
}
}
}
},
"title": {
"type": "string"
},
"trade": {
"type": "object",
"properties": {
"lead_time": {
"type": "array",
"items": {
"type": "object",
"properties": {
"max_quantity": {
"type": "integer"
},
"min_quantity": {
"type": "integer"
},
"process_period_days": {
"type": "integer"
}
}
}
},
"sales_volume": {
"type": "string"
}
}
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"seller": {
"type": "object",
"properties": {
"years": {
"type": "string"
},
"country": {
"type": "string"
},
"company_id": {
"type": "integer"
},
"profile_url": {
"type": "string"
},
"company_name": {
"type": "string"
},
"is_gold_supplier": {
"type": "boolean"
}
}
},
"attributes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"product_id": {
"type": "integer"
},
"locale_host": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Alibaba Search (/alibaba-search)
**GET** `https://api.piloterr.com/v2/alibaba/search`
Alibaba Search API. Query B2B product listings by keyword or search URL across 17 locale subdomains. Request mode returns prices, MOQ, seller data and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Alibaba search URL. **Examples (keyword):** neon **Examples (search URL):** ``` https://www.alibaba.com/trade/search?SearchText=neon ``` ``` https://portuguese.alibaba.com/search/page?SearchText=neon&pro=true ``` **Notes:** - Keyword alone defaults to `www.alibaba.com` (English). - Use `subdomain=french` to force a locale on keyword searches. - Full URLs keep the host locale; `subdomain` is ignored. - Pass each `listing_url` or `product_id` to [Alibaba Product](https://www.piloterr.com/library/alibaba-product). |
| `subdomain` | query | `string` | No | Locale subdomain for keyword searches when `query` is not a full URL. **Examples:** ``` french ``` ``` portuguese ``` ``` french.alibaba.com ``` Ignored when `query` is a full Alibaba URL. Invalid values return `400`. |
| `page` | query | `number` | No | 1-based page number (20 results per page). Default: `1`. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw open-s JSON as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"category": {
"type": "string"
},
"image_url": {
"type": "string"
},
"min_order": {
"type": "string"
},
"price_max": {
"type": "integer"
},
"price_min": {
"type": "number"
},
"seller_id": {
"type": "string"
},
"price_text": {
"type": "string"
},
"product_id": {
"type": "string"
},
"sold_count": {
"type": "integer"
},
"listing_url": {
"type": "string"
},
"seller_name": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "boolean"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# AliExpress Product (/aliexpress-product)
**GET** `https://api.piloterr.com/v2/aliexpress/product`
AliExpress Product API: scrape live `/item/` product pages from a full URL. Get title, images, seller identity, pricing, specs, delivery, and FAQs as JSON via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full AliExpress product page URL (`/item/{id}.html`). **Example:** https://www.aliexpress.com/item/1005009517624640.html **Example (locale subdomain with tracking params):** https://es.aliexpress.com/item/1005012069266068.html?spm=... **Notes:** - Pass the full product URL (not just the numeric ID). - Locale subdomains are supported (`es.`, `fr.`, `de.`, …). - Query params (`spm`, `pdp_ext_f`, …) are preserved in `info.link`. - The page is fetched using the canonical `/item/` URL without query params. - **Rejected (400):** bundle/SSR URLs (`/ssr/`, `productIds=`), non-`/item/` URLs, non-AliExpress domains. - Use **AliExpress Search** to discover `listing_url` values first. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of parsed JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"faqs": {
"type": "array",
"items": {}
},
"info": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"name": {
"type": "string"
},
"rate": {
"type": "integer"
},
"media": {
"type": "array",
"items": {
"type": "string"
}
},
"reviews": {
"type": "integer"
},
"seller_id": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"sold_count": {
"type": "integer"
},
"seller_name": {
"type": "string"
},
"available_count": {
"type": "integer"
}
}
},
"pricing": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"discount": {
"type": "string"
},
"original_price": {
"type": "string"
},
"price_currency": {
"type": "string"
}
}
},
"delivery": {
"type": "object",
"properties": {
"pickup_method": {
"type": "string"
},
"shipping_cost": {
"type": "number"
},
"estimated_delivery": {
"type": "string"
},
"shipping_cost_currency": {
"type": "string"
}
}
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# AliExpress Search (/aliexpress-search)
**GET** `https://api.piloterr.com/v2/aliexpress/search`
AliExpress Search API: search by keyword or wholesale URL and get product listings with prices, ratings, sold counts, and pagination as JSON via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full AliExpress search results URL. **Example (keyword):** meta Resolves to a wholesale search page such as `https://www.aliexpress.com/w/wholesale-meta.html`. **Example (wholesale URL):** https://www.aliexpress.com/w/wholesale-meta.html?spm=a2g0o.productlist.search.0 **Example (page 2):** https://www.aliexpress.com/w/wholesale-meta.html?page=2 **Accepted URL patterns:** `/w/`, `/wholesale`, `searchText=`, `/af/` **Notes:** - Pass a keyword or copy the full search URL from aliexpress.com. - There is no `page` parameter: paginate by adding `?page=2` to the URL. - Use each result `listing_url` or `product_id` with **AliExpress Product** for full PDP and seller data. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"condition": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"sold_count": {
"type": "integer"
},
"listing_url": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "boolean"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# AliExpress Store Product (/aliexpress-store-product)
**GET** `https://api.piloterr.com/v2/aliexpress/store/product`
AliExpress Store Product API: list products from an AliExpress seller store by URL or store ID. Returns store metadata and product cards (~40 SSR items) as JSON via browser rendering. Pagination is not supported.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | AliExpress store URL or numeric store ID. **Example (store URL):** https://www.aliexpress.com/store/1101705614 **Example (all-items page URL):** https://www.aliexpress.com/store/1101705614/pages/all-items.html **Example (store ID only):** 1101705614 **Notes:** - Pass a `/store/{id}` URL, an all-items page URL, or the numeric store ID. - Search URLs and product URLs are rejected (400). - Returns up to ~40 store products from the SSR all-items page (AliExpress store pagination is JavaScript-only and is not supported). - Use each result `listing_url` with **AliExpress Product** for full PDP data. |
| `sort` | query | `string` | No | Optional sort alias for the store all-items page. **Accepted values:** `default`, `bestmatch`, `orders`, `new`, `price` Maps to AliExpress `shop_sortType` query params (e.g. `orders` → `orders_sort`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"sold_count": {
"type": "integer"
},
"listing_url": {
"type": "string"
}
}
}
},
"store_id": {
"type": "string"
},
"seller_id": {
"type": "string"
},
"store_name": {
"type": "string"
},
"owner_member_id": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Allegro Product (/allegro-product)
**GET** `https://api.piloterr.com/v2/allegro/product`
Allegro Product API. Extract full product page data from allegro.pl by URL (`/produkt/`, `/oferta/`, `/ogloszenie/`). Browser rendering, 3 credits per call. Pair with Allegro Search (3 credits) for listing discovery.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full allegro.pl product or offer URL to extract structured PDP data. **Accepted URL formats:** ``` https://allegro.pl/produkt/slug-uuid?offerId=123 ``` ``` https://allegro.pl/oferta/slug-offerId ``` ``` https://allegro.pl/ogloszenie/slug-offerId ``` **Example:** https://allegro.pl/produkt/mufa-ocynkowana-ticka-zlaczka-mufowa-fi-125-do-rur-spiro-wentylacja-6357bbc1-df4f-48ec-8e99-b346a1326f3c?offerId=16509668391 **Notes:** - Paste any `listing_url` returned by [Allegro Search](https://www.piloterr.com/library/allegro-search). - Supports `/produkt/`, `/oferta/` and `/ogloszenie/` paths on allegro.pl. - Inactive or removed listings may return partial data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"sku": {
"type": "string"
},
"url": {
"type": "string"
},
"bars": {
"type": "array",
"items": {}
},
"icon": {
"type": "string"
},
"name": {
"type": "string"
},
"skin": {
"type": "string"
},
"type": {
"type": "integer"
},
"brand": {
"nullable": true
},
"image": {
"type": "string"
},
"offer": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"seo": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"noindex": {
"type": "boolean"
},
"nofollow": {
"type": "boolean"
},
"canonical": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
}
},
"description": {
"type": "string"
}
}
},
"name": {
"type": "string"
},
"view": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"ended": {
"nullable": true
},
"future": {
"nullable": true
},
"snapshot": {
"nullable": true
},
"restricted": {
"nullable": true
}
}
},
"coins": {
"nullable": true
},
"stock": {
"nullable": true
},
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"brand": {
"type": "object",
"properties": {
"logotype": {
"nullable": true
},
"description": {
"nullable": true
}
}
},
"login": {
"type": "string"
},
"banner": {
"nullable": true
},
"company": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"street": {
"type": "string"
},
"country": {
"type": "string"
},
"zip_code": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
},
"verified": {
"type": "boolean"
},
"representative_label": {
"type": "string"
},
"tax_identification_number": {
"type": "string"
},
"national_court_register_number": {
"nullable": true
},
"statistical_identification_number": {
"type": "string"
}
}
},
"contact": {
"type": "object",
"properties": {
"emails": {
"type": "array",
"items": {}
},
"phones": {
"type": "array",
"items": {}
},
"open_hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"days_label": {
"type": "string"
},
"hours_labels": {
"type": "array",
"items": {
"type": "string"
}
},
"days_short_label": {
"type": "string"
}
}
}
}
}
},
"listing": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
}
},
"logotype": {
"nullable": true
},
"vacation": {
"nullable": true
},
"descriptions": {
"type": "object",
"properties": {
"main": {
"nullable": true
},
"additional": {
"nullable": true
}
}
},
"super_seller": {
"nullable": true
},
"privacy_policy": {
"nullable": true
},
"representation": {
"nullable": true
},
"category_listing": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
}
}
}
},
"charity": {
"nullable": true
},
"contact": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"emails": {
"type": "array",
"items": {
"type": "object",
"properties": {
"address": {
"type": "string"
}
}
}
},
"phones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"number": {
"type": "string"
},
"number_label": {
"type": "string"
},
"number_label_masked": {
"type": "string"
}
}
}
}
}
},
"product": {
"type": "object",
"properties": {
"rating": {
"nullable": true
},
"reviews": {
"nullable": true
}
}
},
"rebates": {
"type": "object",
"properties": {
"bundles": {
"type": "array",
"items": {}
}
}
},
"category": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"tree": {
"type": "string"
}
}
},
"delivery": {
"type": "object",
"properties": {
"summary": {
"type": "array",
"items": {
"type": "object",
"properties": {
"icon": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"name": {
"type": "object",
"properties": {
"icon": {
"nullable": true
},
"text": {
"type": "string"
},
"featured": {
"type": "boolean"
}
}
},
"badge": {
"nullable": true
},
"value": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"text_color": {
"nullable": true
},
"context_info": {
"nullable": true
}
}
},
"action": {
"nullable": true
},
"labels": {
"type": "array",
"items": {}
}
}
}
}
}
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"country": {
"type": "string"
},
"province": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
},
"campaigns": {
"type": "object",
"properties": {
"summary": {
"type": "array",
"items": {}
}
}
},
"watchlist": {
"type": "object",
"properties": {
"is_watched": {
"type": "boolean"
}
}
},
"automotive": {
"type": "object",
"properties": {}
},
"info_boxes": {
"type": "array",
"items": {}
},
"parameters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"nullable": true
},
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"nullable": true
},
"url": {
"nullable": true
},
"value_label": {
"type": "string"
}
}
}
}
}
}
},
"attachments": {
"type": "array",
"items": {}
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
},
"publication": {
"type": "object",
"properties": {
"ended_at": {
"nullable": true
},
"ending_at": {
"type": "string"
},
"starting_at": {
"nullable": true
},
"availability": {
"nullable": true
}
}
},
"installments": {
"nullable": true
},
"selling_mode": {
"type": "object",
"properties": {
"tax": {
"nullable": true
},
"cart": {
"nullable": true
},
"auction": {
"nullable": true
},
"buy_now": {
"nullable": true
},
"restricted": {
"nullable": true
},
"advertisement": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency": {
"type": "string"
},
"before_exchange": {
"nullable": true
}
}
},
"price_prefix_label": {
"nullable": true
}
}
},
"one_click_buy": {
"nullable": true
}
}
},
"view_counter": {
"nullable": true
},
"product_series": {
"nullable": true
},
"parameters_groups": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"groups": {
"type": "array",
"items": {
"type": "object",
"properties": {
"icon": {
"nullable": true
},
"name": {
"nullable": true
},
"parameters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"nullable": true
},
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"nullable": true
},
"url": {
"nullable": true
},
"value_label": {
"type": "string"
}
}
}
}
}
}
},
"description": {
"nullable": true
}
}
}
}
}
},
"assortment_category": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"assortment_category_path": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
}
},
"price": {
"type": "string"
},
"route": {
"type": "string"
},
"title": {
"type": "string"
},
"erotic": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Allegro Search (/allegro-search)
**GET** `https://api.piloterr.com/v2/allegro/search`
Allegro Search API. Query product listings by keyword, optional page number, or full allegro.pl listing URL. Returns results with prices, sellers, sponsorship flags and pagination. 3 credits per call.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Allegro listing URL. **Examples (keyword):** ``` mufowa ``` ``` Karty pokemon ``` **Examples (listing URL):** ``` https://allegro.pl/listing?string=mufowa ``` ``` https://allegro.pl/listing?string=mufowa&p=2 ``` **Notes:** - Pass a keyword alone for page 1, or paste a full `allegro.pl/listing` URL. - Use `page` to paginate on keywords (`page=2` → `&p=2`). Overrides `p=` in listing URLs. - `string` is the search term; `p` is the page number in the URL. - Polish diacritics are supported. Results may include sponsored listings. |
| `page` | query | `number` | No | Page number for keyword searches (default `1`). **Example:** 2 Overrides `p=` when `query` is a full listing URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"offer_id": {
"type": "string"
},
"position": {
"type": "integer"
},
"image_url": {
"type": "string"
},
"seller_id": {
"type": "string"
},
"product_id": {
"type": "string"
},
"listing_url": {
"type": "string"
},
"seller_name": {
"type": "string"
},
"is_sponsored": {
"type": "boolean"
},
"delivery_info": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Amazon Product Offer (/amazon-product-offer)
**GET** `https://api.piloterr.com/v2/amazon/product/offer`
Efficiently extract details of third-party seller offers for any Amazon product across multiple marketplaces.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Amazon product ASIN (Amazon Standard Identification Number) to retrieve third-party seller offers for. **Example:** B093C9B1HK **Notes:** - **The ASIN is a 10-character alphanumeric code** visible in the product URL on Amazon (e.g. `amazon.com/dp/B093C9B1HK`). - Only products with active third-party offers will return results: products sold exclusively by Amazon may return an empty list. |
| `domain` | query | `string` | No | Amazon domain extension to query. Controls which Amazon marketplace is targeted. - `com`: Amazon United States (default) - `fr`: Amazon France - `de`: Amazon Germany - `co.uk`: Amazon United Kingdom - `it`: Amazon Italy - `es`: Amazon Spain - `ca`: Amazon Canada - `co.jp`: Amazon Japan - `com.br`: Amazon Brazil - `com.mx`: Amazon Mexico - `in`: Amazon India |
| `page` | query | `number` | No | Page number for paginated offer results. **Note:** Use this parameter to paginate through multiple pages of offers when a product has many third-party sellers. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"asin": {
"type": "string"
},
"price": {
"type": "number"
},
"seller_url": {
"type": "string"
},
"seller_name": {
"type": "string"
},
"delivery_info": {
"type": "object",
"properties": {
"time": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "string"
}
}
},
"discount_percentage": {
"type": "integer"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Amazon Product (/amazon-product)
**GET** `https://api.piloterr.com/v2/amazon/product`
Efficiently extract product details from Amazon for dynamic retail insights, including price, stock, features, and seller data.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Amazon product ASIN (Amazon Standard Identification Number) to retrieve full product details for. **Example:** B09DFCB66S **Notes:** - **The ASIN is a 10-character alphanumeric code** found in the Amazon product URL (e.g. `amazon.com/dp/B09DFCB66S`). - Each ASIN is unique to a specific product and marketplace. |
| `domain` | query | `string` | No | Amazon domain extension to query. Controls which Amazon marketplace is targeted. - `com`: Amazon United States (default) - `fr`: Amazon France - `de`: Amazon Germany - `co.uk`: Amazon United Kingdom - `it`: Amazon Italy - `es`: Amazon Spain - `ca`: Amazon Canada - `co.jp`: Amazon Japan - `com.br`: Amazon Brazil - `com.mx`: Amazon Mexico - `in`: Amazon India |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"asin": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"nullable": true
},
"stock": {
"type": "string"
},
"title": {
"type": "string"
},
"coupon": {
"nullable": true
},
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
}
}
},
"currency": {
"nullable": true
},
"features": {
"type": "array",
"items": {
"type": "string"
}
},
"variants": {
"type": "array",
"items": {}
},
"description": {
"type": "string"
},
"product_information": {
"type": "object",
"properties": {
"asin": {
"type": "string"
},
"rated": {
"type": "string"
},
"batteries": {
"type": "string"
},
"item_weight": {
"type": "string"
},
"manufacturer": {
"type": "string"
},
"release_date": {
"type": "string"
},
"type_of_item": {
"type": "string"
},
"best_sellers_rank": {
"type": "array",
"items": {
"type": "string"
}
},
"item_model_number": {
"type": "string"
},
"product_dimensions": {
"type": "string"
},
"date_first_available": {
"type": "string"
}
}
},
"merchant_information": {
"nullable": true
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Amazon Search (/amazon-search)
**GET** `https://api.piloterr.com/v2/amazon/search`
Efficiently scrape Amazon search results for real-time product data and insights across multiple Amazon marketplaces.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword or phrase to search for products on Amazon. **Example:** playstation 5 **Notes:** - Searches are performed against the marketplace set by the `domain` parameter. - Results include both organic and sponsored (ad) listings. - Combine with `page` to retrieve more results beyond the first page. |
| `domain` | query | `string` | No | Amazon domain extension to query. Controls which Amazon marketplace is searched. - `com`: Amazon United States (default) - `fr`: Amazon France - `de`: Amazon Germany - `co.uk`: Amazon United Kingdom - `it`: Amazon Italy - `es`: Amazon Spain - `ca`: Amazon Canada - `co.jp`: Amazon Japan - `com.br`: Amazon Brazil - `com.mx`: Amazon Mexico - `in`: Amazon India |
| `page` | query | `number` | No | Page number for paginated search results. Each page returns up to 60 results. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"asin": {
"type": "string"
},
"price": {
"nullable": true
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"real_price": {
"type": "number"
},
"reviews_count": {
"type": "integer"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Apartments Property (/apartments-property)
**GET** `https://api.piloterr.com/v2/apartments/property`
Extract a full Apartments.com rental listing from a property URL, including amenities, gallery, description, geolocation, and unit details via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Apartments.com property listing URL. **Example:** https://www.apartments.com/141-e-33rd-st-new-york-ny-unit-si-fl16-id43/p6b4mes/ **Notes:** - Only property URLs on `apartments.com` with an ID slug (e.g. `/p6b4mes/`) are accepted. - Property detail pages are JavaScript SPAs; this endpoint uses website rendering to extract photos, amenities, description, and geolocation. - Copy the listing URL from search results or from apartments.com. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"phone": {
"type": "string"
},
"title": {
"type": "string"
},
"units": {
"type": "array",
"items": {}
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"address": {
"type": "string"
},
"bedrooms": {
"type": "string"
},
"amenities": {
"type": "array",
"items": {
"type": "string"
}
},
"bathrooms": {
"type": "string"
},
"rent_range": {
"nullable": true
},
"description": {
"type": "string"
},
"geolocation": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
}
},
"square_feet": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Apartments Search (/apartments-search)
**GET** `https://api.piloterr.com/v2/apartments/search`
Search Apartments.com rentals from a full city or browse URL and retrieve prices, photos, addresses, phone numbers, and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Apartments.com search or city browse URL. **Example 1 (New York, page 3):** https://www.apartments.com/new-york-ny/3 **Example 2 (page 1):** https://www.apartments.com/new-york-ny/ **Notes:** - Only `apartments.com` listing search URLs are accepted. - Copy the full URL from apartments.com, including city slug and page number in the path (`/new-york-ny/3`). - Pagination uses the page segment in the URL path (`/2/`, `/3/`, ...). `pagination.next_url` points to the next page. - Apartments.com search pages are JavaScript SPAs; this endpoint uses website rendering to scrape listing cards. - Pass each result's `url` to the Apartments Property endpoint for full unit data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"phone": {
"type": "string"
},
"title": {
"type": "string"
},
"address": {
"type": "string"
},
"pricing": {
"type": "array",
"items": {
"type": "object",
"properties": {
"beds": {
"type": "string"
},
"price": {
"type": "string"
}
}
}
},
"has_video": {
"type": "boolean"
},
"min_price": {
"type": "integer"
},
"image_count": {
"type": "integer"
},
"country_code": {
"type": "string"
},
"property_type": {
"type": "string"
},
"street_address": {
"type": "string"
},
"has_virtual_tour": {
"type": "boolean"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"current": {
"type": "integer"
},
"next_url": {
"type": "string"
},
"total_count": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Auchan Product (/auchan-product)
**GET** `https://api.piloterr.com/v2/auchan/product`
Effortlessly extract product details from Auchan for dynamic business insights, including nutritional information, ingredients, ratings, and reviews.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | URL of the Auchan product page to extract data from. **Example:** https://www.auchan.fr/charles-alice-specialite-pommes-mangues-sans-sucres-ajoutes/pr-C1177497 **Notes:** - **Only auchan.fr product URLs are supported** URLs from other regional Auchan sites are not accepted. - The product reference code at the end of the URL (e.g. `pr-C1177497`) uniquely identifies the product. - Private or delisted products may return partial or no data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"image": {
"type": "string"
},
"title": {
"type": "string"
},
"rating": {
"type": "integer"
},
"features": {
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"contact": {
"type": "object",
"properties": {
"exploitant": {
"type": "string"
},
"service_consommateur": {
"type": "string"
}
}
},
"reference": {
"type": "string"
},
"description": {
"type": "string"
},
"ingredients": {
"type": "string"
},
"pays_de_fabrication": {
"type": "string"
},
"valeurs_nutritionnelles": {
"type": "object",
"properties": {
"sel": {
"type": "string"
},
"glucides": {
"type": "string"
},
"proteines": {
"type": "string"
},
"dont_sucres": {
"type": "string"
},
"matieres_grasses": {
"type": "string"
},
"fibres_alimentaires": {
"type": "string"
},
"valeur_energetique_kj": {
"type": "string"
},
"valeur_energetique_kcal": {
"type": "string"
},
"dont_acides_gras_satures": {
"type": "string"
}
}
},
"denomination_legale_de_vente": {
"type": "string"
}
}
},
"attributes": {
"type": "array",
"items": {
"type": "string"
}
},
"reviews_count": {
"type": "integer"
},
"reviews_details": {
"type": "object",
"properties": {
"average_rating": {
"type": "number"
},
"rating_distribution": {
"type": "object",
"properties": {
"1": {
"type": "integer"
},
"2": {
"type": "integer"
},
"3": {
"type": "integer"
},
"4": {
"type": "integer"
},
"5": {
"type": "integer"
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Auchan Search (/auchan-search)
**GET** `https://api.piloterr.com/v2/auchan/search`
Extract Auchan product listings from keyword searches for insightful market analysis and competitive edge.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or Auchan search URL to retrieve product listings. **Accepted formats:** - `pomme`: Plain keyword search - `https://www.auchan.fr/recherche?text=pomme`: Full Auchan search URL **Example:** pomme **Notes:** - **Both keyword and full URL formats are supported.** Use the URL format to reproduce a specific Auchan search with applied filters. - Results include product titles, ratings, review counts, attributes, and images. |
| `page` | query | `number` | No | Page number for paginated search results. **Note:** Use the `pagination.has_next_page` field in the response to determine whether additional pages are available. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"nullable": true
},
"image": {
"nullable": true
},
"title": {
"type": "string"
},
"rating": {
"type": "integer"
},
"attributes": {
"type": "array",
"items": {
"type": "string"
}
},
"reviews_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Autodoc Product Related (/autodoc-product-related)
**GET** `https://api.piloterr.com/v2/autodoc/product/related`
Vehicle fitment lines per maker for an Autodoc product. All parameters from Product `fetch.params` are required.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Same HTTPS product page URL as Product. **Example:** `https://www.auto-doc.fr/ridex/7999541` |
| `maker_id` | query | `number` | Yes | TecDoc maker id from `compatibility.cars[]` / `compatibility.trucks[]`, e.g. `93` for RENAULT. |
| `vehicle_type` | query | `string` | Yes | Required. `pkw` (passenger cars) or `lkw` (commercial). Must match the compatibility row. |
| `maker_name` | query | `string` | Yes | Required maker label from compatibility, e.g. `RENAULT`. Used to parse vehicle lines. |
| `product_id` | query | `number` | Yes | Required article id from Product response or URL path, e.g. `7999541`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"models": {
"type": "array",
"items": {
"type": "string"
}
},
"maker_id": {
"type": "integer"
},
"vehicles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"raw": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"end_date": {
"type": "string"
},
"model_id": {
"type": "integer"
},
"start_date": {
"type": "string"
}
}
}
},
"product_id": {
"type": "integer"
},
"product_url": {
"type": "string"
},
"vehicle_type": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Autodoc Product (/autodoc-product)
**GET** `https://api.piloterr.com/v2/autodoc/product`
Scrape Autodoc product pages: price, specs, EAN, images, compatibility, and TecDoc-related vehicle data via the Related endpoint.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | HTTPS URL of an Autodoc product page. **Example:** https://www.auto-doc.fr/ridex/7999541 France uses **`auto-doc.fr`**, not `autodoc.fr`. Response includes `compatibility` with `fetch.params` for Related. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"name": {
"type": "string"
},
"price": {
"type": "number"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "integer"
},
"seller": {
"type": "string"
},
"brand_no": {
"type": "integer"
},
"currency": {
"type": "string"
},
"features": {
"type": "array",
"items": {
"type": "string"
}
},
"subtitle": {
"type": "string"
},
"reference": {
"type": "string"
},
"seller_id": {
"type": "integer"
},
"generic_id": {
"type": "integer"
},
"product_id": {
"type": "integer"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"oem_numbers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"label": {
"type": "string"
}
}
}
},
"product_url": {
"type": "string"
},
"seo_summary": {
"type": "object",
"properties": {
"moteurs": {
"type": "string"
},
"modeles_de_voitures": {
"type": "string"
},
"annees_de_fabrication": {
"type": "string"
},
"numero(s)_de_piece_oe": {
"type": "string"
},
"puissance_(kilowatts)": {
"type": "string"
},
"numero_d_article_du_fabricant": {
"type": "string"
},
"puissance_du_moteur_(chevaux)": {
"type": "string"
}
}
},
"availability": {
"type": "string"
},
"generic_name": {
"type": "string"
},
"goods_ending": {
"type": "integer"
},
"compatibility": {
"type": "object",
"properties": {
"cars": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fetch": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"params": {
"type": "object",
"properties": {
"query": {
"type": "string"
},
"maker_id": {
"type": "integer"
},
"maker_name": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"vehicle_type": {
"type": "string"
}
}
}
}
},
"maker": {
"type": "string"
},
"maker_id": {
"type": "integer"
},
"vehicle_type": {
"type": "string"
}
}
}
},
"trucks": {
"type": "array",
"items": {}
}
}
},
"has_360_photo": {
"type": "boolean"
},
"reviews_count": {
"type": "integer"
},
"specifications": {
"type": "object",
"properties": {
"etat": {
"type": "string"
},
"hauteur": {
"type": "string"
},
"perfore": {
"type": "string"
},
"surface": {
"type": "string"
},
"diametre": {
"type": "string"
},
"materiel": {
"type": "string"
},
"fabricant": {
"type": "string"
},
"traitement": {
"type": "string"
},
"numero_de_ean": {
"type": "string"
},
"nombre_de_trous": {
"type": "string"
},
"numero_d_article": {
"type": "string"
},
"annee_a_partir_de": {
"type": "string"
},
"cote_d_assemblage": {
"type": "string"
},
"cercle_de_percage_ø": {
"type": "string"
},
"diametre_du_centrage": {
"type": "string"
},
"type_de_disque_de_frein": {
"type": "string"
},
"epaisseur_du_disque_de_frein": {
"type": "string"
},
"diametre_d_alesage_du_boulon_de_roue": {
"type": "string"
},
"article_complementaire_info_complementaire_2": {
"type": "string"
}
}
},
"rating_percentage": {
"type": "integer"
},
"discount_percentage": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Autodoc Search (/autodoc-search)
**GET** `https://api.piloterr.com/v2/autodoc/search`
Search Autodoc by URL: keyword pages use `/search?keyword=…` (e.g. `frein` on auto-doc.fr). Extract product cards, prices, and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Autodoc search/listing URL, or path only when `domain` sets the shop. **Typical keyword search (full URL, `domain` ignored):** ``` https://www.auto-doc.fr/search?keyword=frein ``` ``` https://www.autodoc.de/search?keyword=bremsen ``` **Path + `domain`:** - `query`: `search?keyword=frein` - `domain`: `auto-doc.fr` **Rules:** - If `query` starts with `http://` or `https://`, `domain` is ignored. - If `query` has no host, `domain` must be an allowed shop hostname (no `www.`, no `https://`). - France uses **`auto-doc.fr`**, not `autodoc.fr`. |
| `domain` | query | `string` | No | Regional shop hostname without `https://` or `www.` (e.g. `auto-doc.fr`, `autodoc.de`). Omit to default to `autodoc.de`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"price": {
"type": "number"
},
"seller": {
"type": "string"
},
"brand_no": {
"type": "string"
},
"discount": {
"type": "integer"
},
"360_photo": {
"type": "string"
},
"reference": {
"type": "string"
},
"seller_id": {
"type": "integer"
},
"product_id": {
"type": "string"
},
"product_url": {
"type": "string"
},
"availability": {
"type": "string"
},
"generic_name": {
"type": "string"
},
"product_image": {
"type": "string"
},
"reviews_count": {
"type": "integer"
},
"original_price": {
"type": "number"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# AutoScout24 Ad (/autoscout24-ad)
**GET** `https://api.piloterr.com/v2/autoscout24/ad`
Extract full details from an AutoScout24 car listing, including price, vehicle specifications, seller info, and equipment.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full AutoScout24 car listing URL from any supported domain. **Supported domains:** `.de`, `.be`, `.es`, `.fr`, `.it`, `.lu`, `.nl`, `.at`, `.bg`, `.cz`, `.com`, `.hr`, `.pl`, `.ro`, `.ru`, `.se`, `.com.tr`, `.com.ua`, `.hu` **Example:** https://www.autoscout24.fr/offres/peugeot-208-puretech-82ch-s-s-bvm5-allure-1-ere-main-distribut-essence-blanc-51a0c24c-84e6-4ed8-af6b-1b8ef193ae84 **Note:** The UUID at the end of the URL identifies the specific listing. Expired ads will return an error. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"price_formatted": {
"type": "string"
},
"price_evaluation": {
"type": "integer"
}
}
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"prices": {
"type": "object",
"properties": {
"dealer": {
"type": "object",
"properties": {
"price": {
"type": "string"
},
"category": {
"type": "integer"
},
"price_raw": {
"type": "integer"
},
"negotiable": {
"type": "boolean"
},
"evaluation_ranges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"maximum": {
"type": "integer"
},
"minimum": {
"nullable": true
},
"category": {
"type": "integer"
}
}
}
}
}
}
}
},
"seller": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"phone": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"country": {
"type": "string"
}
}
}
}
},
"vehicle": {
"type": "object",
"properties": {
"fuel": {
"type": "string"
},
"make": {
"type": "string"
},
"year": {
"type": "integer"
},
"color": {
"type": "string"
},
"model": {
"type": "string"
},
"mileage": {
"type": "integer"
},
"power_kw": {
"type": "integer"
},
"transmission": {
"type": "string"
},
"engine_displacement": {
"type": "string"
}
}
},
"equipment": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# AutoScout24 Search (/autoscout24-search)
**GET** `https://api.piloterr.com/v2/autoscout24/search`
Search for car listings on AutoScout24 by URL, returning paginated results with vehicle details, pricing, and seller info.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full AutoScout24 search URL from any supported domain. Filter parameters, make/model, and pagination in the URL are all respected. **Supported domains:** `.de`, `.be`, `.es`, `.fr`, `.it`, `.lu`, `.nl`, `.at`, `.bg`, `.cz`, `.com`, `.hr`, `.pl`, `.ro`, `.ru`, `.se`, `.com.tr`, `.com.ua`, `.hu` **Example:** https://www.autoscout24.fr/lst/volkswagen/golf?atype=C **Notes:** - Pagination is embedded in the URL (e.g. append `&page=2` for the next page) - Filters like fuel type, price range, mileage, and year can be included in the URL |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"phones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"call_to": {
"type": "string"
},
"phone_type": {
"type": "string"
}
}
}
},
"company_name": {
"type": "string"
},
"contact_name": {
"type": "string"
}
}
},
"ad_tier": {
"type": "string"
},
"vehicle": {
"type": "object",
"properties": {
"fuel": {
"type": "string"
},
"make": {
"type": "string"
},
"type": {
"type": "string"
},
"model": {
"type": "string"
},
"variant": {
"type": "string"
},
"transmission": {
"type": "string"
},
"mileage_in_km": {
"type": "string"
},
"model_version_input": {
"type": "string"
},
"engine_displacement_in_c_c_m": {
"type": "string"
}
}
},
"location": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
},
"tracking": {
"type": "object",
"properties": {
"price": {
"type": "string"
},
"mileage": {
"type": "string"
},
"fuel_type": {
"type": "string"
},
"price_label": {
"type": "string"
},
"first_registration": {
"type": "string"
}
}
},
"available_now": {
"type": "boolean"
},
"vehicle_details": {
"type": "object",
"properties": {
"gearbox": {
"type": "string"
},
"calendar": {
"type": "string"
},
"speedometer": {
"type": "string"
},
"mileage_odometer": {
"type": "string"
}
}
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Autotrader Ad (/autotrader-ad)
**GET** `https://api.piloterr.com/v2/autotrader/ad`
Extract full vehicle details from an AutoTrader UK car-details URL, including price, specs, gallery, seller, and description via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full AutoTrader UK car details URL. **Example:** https://www.autotrader.co.uk/car-details/202606053030491 **Notes:** - Only `/car-details/{id}` listing URLs on autotrader.co.uk are accepted. - Query parameters in the URL are ignored; the listing ID in the path is used. - AutoTrader advert pages are JavaScript SPAs; this endpoint uses website rendering to scrape price, specs, gallery, seller, and description from the rendered page. - Copy the listing URL from search results or from autotrader.co.uk. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"seller": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"phone": {
"type": "string"
},
"rating": {
"type": "number"
},
"location": {
"type": "string"
}
}
},
"subtitle": {
"nullable": true
},
"description": {
"type": "string"
},
"price_amount": {
"type": "integer"
},
"specifications": {
"type": "object",
"properties": {
"doors": {
"type": "string"
},
"seats": {
"type": "string"
},
"engine": {
"type": "string"
},
"gearbox": {
"type": "string"
},
"mileage": {
"type": "string"
},
"body_type": {
"type": "string"
},
"fuel_type": {
"type": "string"
},
"body_colour": {
"type": "string"
},
"registration": {
"type": "string"
},
"emission_class": {
"type": "string"
}
}
},
"reserve_available": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Autotrader Search (/autotrader-search)
**GET** `https://api.piloterr.com/v2/autotrader/search`
Search AutoTrader UK listings from a car-search URL and retrieve prices, specs, photos, seller info, and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full AutoTrader UK car search URL. **Example 1 (used cars near London):** https://www.autotrader.co.uk/car-search?channel=cars&postcode=W1F%208EB&price_search_type=total&sort=relevance&page=1 **Example 2 (page 2):** https://www.autotrader.co.uk/car-search?channel=cars&postcode=W1F%208EB&price_search_type=total&sort=relevance&page=2 **Notes:** - Only `autotrader.co.uk` `/car-search` URLs are accepted. - Copy the full URL from autotrader.co.uk, including filters (`postcode`, `make`, `price`, etc.). - Pagination via `?page=2` in the URL. - AutoTrader search pages are JavaScript SPAs; this endpoint uses website rendering to scrape the rendered listing cards. - Use each result's `url` with the AutoTrader Ad endpoint for full vehicle data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"year": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"mileage": {
"type": "string"
},
"location": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"seller_type": {
"type": "string"
},
"price_amount": {
"type": "integer"
},
"attention_grabber": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"next_url": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Best Buy Product (/bestbuy-product)
**GET** `https://api.piloterr.com/v2/bestbuy/product`
Get a Best Buy US or Canada product page: title, price, brand, stock, images, categories and specs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Best Buy product URL. **Examples:** ``` https://www.bestbuy.com/product/gigabyte-aero-x16-copilot-pc-165hz-wqxga-rtx-5070-ryzen-ai-7-350-1tb-ssd-16gb-ddr5-windows-11-lunar-white/J3GWPQC2RQ ``` ``` https://www.bestbuy.ca/fr-ca/produit/machine-a-espresso-automatique-800-de-philips-avec-moussoir-a-lait-noir-mat-exclusivite-best-buy/16691494 ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"sku": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"region": {
"type": "string"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"categories": {
"type": "array",
"items": {
"type": "string"
}
},
"product_id": {
"type": "string"
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Best Buy Search (/bestbuy-search)
**GET** `https://api.piloterr.com/v2/bestbuy/search`
Search Best Buy US or Canada by keyword or search URL. Returns product listings with price, rating and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword or full Best Buy search URL. **Examples:** ``` machine ``` ``` https://www.bestbuy.com/site/searchpage.jsp?st=machine ``` ``` https://www.bestbuy.ca/fr-ca/chercher?search=machine ``` A full Best Buy URL auto-detects `region`. |
| `region` | query | `string` | No | Best Buy country when `query` is not a full URL (default: `com`). **Supported:** - `com` → www.bestbuy.com, USD - `ca` → www.bestbuy.ca, CAD |
| `page` | query | `number` | No | Page number (default: `1`). |
| `sort` | query | `string` | No | Sort order (default: `best_selling`). **Supported:** `best_selling`, `best_discount` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"condition": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Bing Search (/bing-search)
**GET** `https://api.piloterr.com/v2/bing/search`
Scrape real-time Bing search results with full control over location, language, and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The search query to execute on Bing. Supports all Bing advanced search operators. **Examples:** - `artificial intelligence`: standard keyword search - `site:github.com python scraper`: restrict results to a specific domain - `intitle:"machine learning"`: match the page title - `inurl:blog python`: match part of the URL **Example:** artificial intelligence news 2024 |
| `page` | query | `number` | No | Page number of results to retrieve. Used for pagination. - `1`: First page (default) - `2`: Second page - `3`: Third page, etc. |
| `num` | query | `number` | No | Number of results per page. **Minimum:** `10`, **Maximum:** `50`. **Note:** This parameter is only a suggestion and might not reflect the actual number of results returned. |
| `location` | query | `string` | No | City or region from which the search should originate. If multiple locations match, the most popular one is selected. **Note:** Cannot be used together with `lat`/`lon`. It is recommended to specify location at the city level. **Example:** Austin |
| `lat` | query | `number` | No | GPS latitude coordinate for the search origin. **Example:** 48.8566 |
| `lon` | query | `number` | No | GPS longitude coordinate for the search origin. **Example:** 2.3522 |
| `mkt` | query | `string` | No | Market code combining language and country, in the form `-`. Defines where the results come from. - `en-US`: English, United States (default) - `fr-FR`: French, France - `de-DE`: German, Germany - `es-ES`: Spanish, Spain **Note:** Cannot be used together with `cc`. |
| `cc` | query | `string` | No | Two-letter ISO 3166-1 country code defining the country to search from. - `us`: United States - `de`: Germany - `gb`: United Kingdom - `fr`: France **Note:** Cannot be used together with `mkt`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {}
},
"has_next_page": {
"type": "boolean"
}
}
},
"organic_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"snippet": {
"nullable": true
},
"position": {
"type": "integer"
},
"displayed_link": {
"type": "string"
},
"snippet_matched": {
"type": "array",
"items": {}
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"cc": {
"nullable": true
},
"lat": {
"nullable": true
},
"lon": {
"nullable": true
},
"mkt": {
"nullable": true
},
"num": {
"nullable": true
},
"page": {
"type": "integer"
},
"engine": {
"type": "string"
},
"location": {
"nullable": true
},
"bing_domain": {
"type": "string"
}
}
},
"search_information": {
"type": "object",
"properties": {
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Brave Search (/brave-search)
**GET** `https://api.piloterr.com/v2/brave/search`
Access the Brave search engine results directly through our efficient Piloterr service for reliable data scraping.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The search query to execute on Brave Search. Supports all standard Brave search operators. **Examples:** - `machine learning`: standard keyword search - `site:github.com scraper`: restrict results to a specific domain - `intitle:"growth hacking"`: match the page title - `inurl:blog python`: match part of the URL **Example:** best programming languages 2024 |
| `page` | query | `number` | No | Page number of results to retrieve. Used for pagination. - `1`: First page (default) - `2`: Second page - `3`: Third page, etc. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"organic_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"snippet": {
"type": "string"
},
"position": {
"type": "integer"
},
"displayed_link": {
"type": "string"
},
"snippet_matched": {
"type": "array",
"items": {}
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"page": {
"type": "integer"
},
"engine": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Carrefour Product (/carrefour-product)
**GET** `https://api.piloterr.com/v2/carrefour/product`
Extract full product details from a Carrefour product page, including price, brand, nutritional info, packaging, and availability.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Carrefour product page URL from `carrefour.fr`. **Example:** https://www.carrefour.fr/p/oeufs-de-caille-le-gaulois-3266980075934 **Notes:** - **Only `carrefour.fr` is currently supported.** Other domains (e.g. `carrefour.es`, `carrefour.it`) are not supported. - The URL must point to a product detail page (starting with `/p/`). |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"brand": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"packaging": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
},
"label": {
"type": "string"
},
"level": {
"type": "integer"
}
}
}
},
"nutriscore": {
"nullable": true
},
"more_offers": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"best_offer_id": {
"nullable": true
},
"best_offer_price": {
"nullable": true
}
}
},
"price_float": {
"type": "number"
},
"short_title": {
"type": "string"
},
"business_type": {
"type": "string"
},
"is_best_seller": {
"type": "boolean"
},
"price_per_unit": {
"type": "string"
},
"customer_reviews": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"rates": {
"nullable": true
},
"average": {
"type": "integer"
}
}
},
"price_per_unit_float": {
"type": "number"
},
"price_per_unit_measure": {
"type": "string"
},
"price_per_unit_currency": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Carrefour Search (/carrefour-search)
**GET** `https://api.piloterr.com/v2/carrefour/search`
Search for products on Carrefour by keyword or URL, returning structured listings with prices, ratings, and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Carrefour search page URL. **Accepted formats:** - `pomme`: keyword search - `https://www.carrefour.fr/s?q=pomme`: full search URL **Example:** pomme **Note:** Only `carrefour.fr` is currently supported. |
| `domain` | query | `string` | No | Carrefour domain to search on. - `fr`: carrefour.fr (default, only supported domain) **Note:** Only the `fr` domain is currently supported. |
| `page` | query | `number` | No | Page number to retrieve. **Example:** Pass `2` to retrieve the second page of results. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"currency": {
"type": "string"
},
"sponsored": {
"type": "boolean"
},
"price_float": {
"type": "number"
},
"reviews_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Carrefour Suggest (/carrefour-suggest)
**GET** `https://api.piloterr.com/v2/carrefour/suggest`
Get product suggestions from Carrefour by keyword or EAN, returning matching products with pricing, availability, and product details.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or EAN code to get product suggestions from Carrefour. **Accepted formats:** - `coca`: keyword (e.g. brand name, product type) - `3276550063551`: EAN barcode **Example (keyword):** coca **Example (EAN):** 3276550063551 **Note:** Only `carrefour.fr` is currently supported. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"links": {
"type": "object",
"properties": {
"self": {
"type": "string"
},
"reviews": {
"type": "string"
}
}
},
"attributes": {
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"slug": {
"type": "string"
},
"brand": {
"type": "string"
},
"title": {
"type": "string"
},
"format": {
"type": "string"
},
"offers": {
"type": "object",
"properties": {
"5000112611861": {
"type": "object",
"properties": {
"0261_150_6": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"attributes": {
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"perunit": {
"type": "number"
},
"perunitlabel": {
"type": "string"
},
"unitofmeasure": {
"type": "string"
}
}
},
"availability": {
"type": "object",
"properties": {
"purchasable": {
"type": "boolean"
}
}
}
}
}
}
}
}
}
}
},
"packaging": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
},
"label": {
"type": "string"
},
"level": {
"type": "integer"
}
}
}
},
"nutriscore": {
"nullable": true
},
"shorttitle": {
"type": "string"
},
"businesstype": {
"type": "string"
},
"customerreviews": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"rates": {
"nullable": true
},
"average": {
"type": "number"
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Cdiscount Product Offer (/cdiscount-product-offer)
**GET** `https://api.piloterr.com/v2/cdiscount/product/offer`
Retrieve all marketplace seller offers for a Cdiscount product, including prices, seller ratings, shipping costs, and availability.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Cdiscount product offer page URL. Pass the `multiple_offer_url` value returned by the Cdiscount Product API. **Example:** https://www.cdiscount.com/mp-352880-auc3283981039048.html **How to get this URL:** - Call the Cdiscount Product API with any product URL. - Extract the `multiple_offer_url` field from the response. - Use that URL as the `query` parameter here. **Note:** Only URLs with the pattern `/mp-{id}-{ean}.html` are accepted. Regular product page URLs will not work. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"seller": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"rating": {
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"reviews_count": {
"type": "integer"
},
"score_percentage": {
"type": "integer"
}
}
},
"sales_count": {
"type": "integer"
}
}
},
"condition": {
"type": "string"
},
"shipped_by": {
"type": "string"
},
"availability": {
"type": "string"
},
"delivery_time": {
"type": "string"
},
"shipping_cost": {
"type": "string"
},
"shipping_country": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Cdiscount Product (/cdiscount-product)
**GET** `https://api.piloterr.com/v2/cdiscount/product`
Extract full product details from a Cdiscount product page, including price, seller, ratings, specifications, and offers.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Cdiscount product page URL. **Example:** https://www.cdiscount.com/informatique/ordinateurs-pc-portables/acemagic-pc-portables-gaming/f-1070992-aaaph06865.html **Notes:** - Only standard Cdiscount product URLs are accepted (e.g. `/f-{category}-{id}.html`). - Offer comparison pages (`/mp-{id}-{ean}.html`) should be used with the Cdiscount Product Offer endpoint instead. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"ean": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "integer"
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"rating": {
"type": "integer"
},
"sales_count": {
"type": "integer"
},
"reviews_count": {
"type": "integer"
}
}
},
"is_cdav": {
"type": "boolean"
},
"currency": {
"type": "string"
},
"offer_id": {
"type": "integer"
},
"condition": {
"type": "string"
},
"description": {
"type": "string"
},
"is_available": {
"type": "boolean"
},
"other_offers": {
"type": "object",
"properties": {
"total_offers": {
"type": "integer"
},
"lowest_price_new": {
"type": "number"
},
"new_offers_count": {
"type": "integer"
},
"lowest_price_used": {
"nullable": true
},
"used_offers_count": {
"type": "integer"
}
}
},
"delivery_info": {
"type": "string"
},
"reviews_count": {
"type": "integer"
},
"original_price": {
"type": "number"
},
"multiple_offer_url": {
"type": "string"
},
"multiple_offer_text": {
"type": "string"
},
"multiple_offer_starting_price": {
"type": "number"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Cdiscount Search (/cdiscount-search)
**GET** `https://api.piloterr.com/v2/cdiscount/search`
Extract product listings from Cdiscount search results or category pages for market intelligence and competitive analysis.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Cdiscount search results page URL. Use any URL from a search or category browse on cdiscount.com. **Example:** https://www.cdiscount.com/search/10/laptop.html **Notes:** - You can use keyword search URLs (e.g. `/search/10/{keyword}.html`) or category browse URLs (e.g. `/informatique/ordinateurs-pc-portables/...`) - Pagination and filter parameters present in the URL are respected. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"is_new": {
"type": "boolean"
},
"rating": {
"type": "integer"
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"is_cdav": {
"type": "boolean"
},
"brand_id": {
"type": "integer"
},
"currency": {
"type": "string"
},
"offer_id": {
"type": "integer"
},
"condition": {
"type": "string"
},
"category_id": {
"type": "integer"
},
"is_sponsored": {
"type": "boolean"
},
"min_quantity": {
"type": "integer"
},
"category_path": {
"type": "string"
},
"department_id": {
"type": "integer"
},
"free_shipping": {
"type": "boolean"
},
"reviews_count": {
"type": "integer"
},
"original_price": {
"type": "number"
},
"new_offer_quantity": {
"type": "integer"
},
"new_offer_min_price": {
"type": "integer"
},
"used_offer_quantity": {
"type": "integer"
},
"used_offer_min_price": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Chewy Product (/chewy-product)
**GET** `https://api.piloterr.com/v2/chewy/product`
Retrieve full product details from a Chewy product page URL, including pricing, variants, ratings, and availability.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Chewy product page URL to scrape. **Example:** https://www.chewy.com/dp/371150 **Notes:** - The URL must be a valid Chewy product detail page (starts with `https://www.chewy.com/dp/` or a full product slug URL). - Variant-level item IDs (e.g. `/dp/116881`) are supported: the API will return the full product graph including all variants. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ROOT_QUERY": {
"type": "object",
"properties": {
"pdp": {
"type": "object",
"properties": {
"__typename": {
"type": "string"
},
"featureToggles": {
"type": "object",
"properties": {
"__typename": {
"type": "string"
},
"qnaEnabled": {
"type": "boolean"
},
"reviewsEnabled": {
"type": "boolean"
},
"fbtWidgetEnabled": {
"type": "boolean"
},
"realTimeDealsEnabled": {
"type": "boolean"
},
"loyaltyFeaturesEnabled": {
"type": "boolean"
}
}
}
}
},
"__typename": {
"type": "string"
},
"currentUser": {
"nullable": true
},
"item({\"id\":\"371150\"})": {
"type": "object",
"properties": {
"__ref": {
"type": "string"
}
}
},
"product({\"id\":\"371138\"})": {
"type": "object",
"properties": {
"__ref": {
"type": "string"
}
}
}
}
},
"Item:SXRlbTozNzExNTA=": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"gtin": {
"type": "string"
},
"name": {
"type": "string"
},
"entryID": {
"type": "string"
},
"inStock": {
"type": "boolean"
},
"isFresh": {
"type": "boolean"
},
"isBundle": {
"type": "boolean"
},
"isFrozen": {
"type": "boolean"
},
"mapPrice": {
"type": "string"
},
"isInStock": {
"type": "boolean"
},
"isVetDiet": {
"type": "boolean"
},
"onSpecial": {
"type": "boolean"
},
"__typename": {
"type": "string"
},
"dimensions": {
"type": "string"
},
"isGiftCard": {
"type": "boolean"
},
"isLiveFeed": {
"type": "boolean"
},
"partNumber": {
"type": "string"
},
"description": {
"type": "string"
},
"isPublished": {
"type": "boolean"
},
"keyBenefits": {
"type": "array",
"items": {
"type": "string"
}
},
"perUnitPrice": {
"type": "string"
},
"autoshipPrice": {
"type": "string"
},
"isUnavailable": {
"type": "boolean"
},
"isPrescription": {
"type": "boolean"
},
"advertisedPrice": {
"type": "string"
},
"shippingMessage": {
"type": "string"
},
"autoshipDiscountPct": {
"type": "string"
},
"unitOfMeasureString": {
"type": "string"
}
}
},
"Product:UHJvZHVjdDozNzExMzg=": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"rating": {
"type": "number"
},
"entryID": {
"type": "string"
},
"__typename": {
"type": "string"
},
"isEnsemble": {
"type": "boolean"
},
"partNumber": {
"type": "string"
},
"description": {
"type": "string"
},
"ratingCount": {
"type": "integer"
},
"manufacturerName": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Chewy Search (/chewy-search)
**GET** `https://api.piloterr.com/v2/chewy/search`
Scrape Chewy search, brand, or category pages to retrieve a list of products with pricing, ratings, and availability.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Chewy search or browse URL to scrape results from. Supported URL types: - `search`: `https://www.chewy.com/s?query=dog+food` - `brand page`: `https://www.chewy.com/brands/feliway-6614` - `category page`: `https://www.chewy.com/b/dry-food-294` **Example:** https://www.chewy.com/brands/feliway-6614 **Notes:** - The URL must be a valid Chewy listing, search, or brand page. - Pagination is handled internally; the API returns results from the first page of the given URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ppu": {
"type": "string"
},
"href": {
"type": "string"
},
"name": {
"type": "string"
},
"image": {
"type": "string"
},
"is_ad": {
"type": "boolean"
},
"is_atf": {
"type": "boolean"
},
"is_new": {
"type": "boolean"
},
"rating": {
"type": "number"
},
"is_deal": {
"type": "boolean"
},
"in_stock": {
"type": "boolean"
},
"is_fresh": {
"type": "boolean"
},
"is_bundle": {
"type": "boolean"
},
"is_frozen": {
"type": "boolean"
},
"description": {
"type": "string"
},
"part_number": {
"type": "string"
},
"manufacturer": {
"type": "string"
},
"rating_count": {
"type": "integer"
},
"strike_price": {
"type": "string"
},
"display_price": {
"type": "string"
},
"autoship_price": {
"type": "string"
},
"is_rx_vet_diet": {
"type": "boolean"
},
"is_discontinued": {
"type": "boolean"
},
"is_prescription": {
"type": "boolean"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Company Database (/company)
**GET** `https://api.piloterr.com/v2/company`
Retrieve comprehensive company data including financials, technologies, social profiles, and location by domain name.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The domain name of the company to look up. Accepts multiple formats: - `gucci.com`: bare domain name - `https://gucci.com`: full URL format - `www.gucci.com`: www-prefixed domain **Note:** Using a unique domain name ensures accuracy and avoids ambiguity that may arise from common company names or spelling variations. **Example:** gucci.com |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"founded": {
"type": "integer"
},
"revenue": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"lat": {
"type": "string"
},
"lng": {
"type": "string"
},
"city": {
"type": "string"
},
"address": {
"nullable": true
},
"country": {
"type": "string"
},
"postcode": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
},
"domain_tld": {
"type": "string"
},
"industries": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_at": {
"nullable": true
},
"description": {
"type": "string"
},
"domain_name": {
"type": "string"
},
"staff_range": {
"type": "string"
},
"phone_number": {
"type": "string"
},
"technologies": {
"type": "array",
"items": {
"type": "string"
}
},
"business_type": {
"nullable": true
},
"social_networks": {
"type": "object",
"properties": {
"twitter": {
"type": "string"
},
"youtube": {
"type": "string"
},
"facebook": {
"type": "string"
},
"linkedin": {
"type": "string"
},
"instagram": {
"type": "string"
},
"pinterest": {
"type": "string"
},
"twitter_id": {
"type": "string"
},
"youtube_id": {
"type": "string"
},
"facebook_id": {
"type": "string"
},
"instagram_id": {
"type": "string"
},
"pinterest_id": {
"type": "string"
},
"linkedin_id_alpha": {
"type": "string"
},
"linkedin_id_numeric": {
"type": "integer"
},
"linkedin_sales_navigator": {
"type": "string"
}
}
},
"monthly_visitors": {
"type": "string"
},
"technology_categories": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Copart Lot (/copart-lot)
**GET** `https://api.piloterr.com/v2/copart/lot`
Copart Lot API: retrieve structured auction lot data by lot number or URL. Specs, damage, bids, photos, yard location and sale status across regional Copart sites.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Copart lot number or full lot URL. **Examples:** ``` 55295666 ``` ``` 55295666 + region=uk ``` ``` https://www.copart.co.uk/lot/55295666/clean-title-2014-porsche-panamera ``` |
| `region` | query | `string` | No | Regional Copart site when `query` is a lot number only. Defaults to US. **Examples:** ``` ca ``` ``` uk ``` ``` de ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"lic": {
"type": "array",
"items": {
"type": "string"
}
},
"url": {
"type": "string"
},
"fuel": {
"type": "string"
},
"keys": {
"type": "string"
},
"make": {
"type": "string"
},
"year": {
"type": "integer"
},
"color": {
"type": "string"
},
"drive": {
"type": "string"
},
"model": {
"type": "string"
},
"title": {
"type": "string"
},
"engine": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"currency": {
"type": "string"
},
"latitude": {
"type": "number"
},
"lot_sold": {
"type": "boolean"
},
"odometer": {
"type": "integer"
},
"image_url": {
"type": "string"
},
"longitude": {
"type": "number"
},
"sale_date": {
"type": "string"
},
"yard_city": {
"type": "string"
},
"yard_name": {
"type": "string"
},
"bid_status": {
"type": "string"
},
"buy_it_now": {
"type": "boolean"
},
"run_status": {
"type": "string"
},
"title_code": {
"type": "string"
},
"yard_state": {
"type": "string"
},
"current_bid": {
"type": "integer"
},
"model_group": {
"type": "string"
},
"sale_status": {
"type": "string"
},
"vin_partial": {
"type": "string"
},
"yard_number": {
"type": "integer"
},
"last_updated": {
"type": "string"
},
"title_status": {
"type": "string"
},
"transmission": {
"type": "string"
},
"vehicle_type": {
"type": "string"
},
"yard_country": {
"type": "string"
},
"primary_damage": {
"type": "string"
},
"buy_it_now_price": {
"type": "integer"
},
"secondary_damage": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Copart Search (/copart-search)
**GET** `https://api.piloterr.com/v2/copart/search`
Copart Search API: search auction lots by keyword or URL across Copart regional sites. Returns lot listings and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Copart search URL. **Examples:** ``` chevrolet ``` ``` audi + region=ca ``` ``` https://www.copart.co.uk/lotSearchResults?query=bmw ``` Use **Copart Lot** on any result `id` or `url` for full details. |
| `region` | query | `string` | No | Copart regional site when `query` is a keyword or lot number only. Defaults to US. **Examples:** ``` ca ``` ``` uk ``` ``` de ``` |
| `page` | query | `number` | No | Page number (default: 1). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"region": {
"type": "string"
},
"image_url": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"region": {
"type": "string"
},
"keyword": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Costco Product (/costco-product)
**GET** `https://api.piloterr.com/v2/costco/product`
Costco Product API: fetch full product page data from a product ID or URL across regional Costco sites. Returns pricing, availability, ratings, images and breadcrumbs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Costco product ID or full product URL. **Examples:** ``` 4000266385 ``` ``` 4000268598 + region=ca ``` ``` https://www.costco.com/p/-/mohawk-home-6mm-thick-94in-x-478in-22-mil-waterproof-luxury-vinyl-plank-flooring-1565-sq-ftctn/4000266385 ``` ``` https://www.costco.ca/nom.produit.product.4000268598.html ``` |
| `region` | query | `string` | No | Regional Costco site. Accepts a region code (`us`, `fr`, `ca`, `uk`, …) or full host (`www.costco.fr`). Defaults to US (`www.costco.com`) when omitted. **Examples:** ``` fr ``` ``` ca ``` ``` www.costco.co.uk ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"region": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"item_number": {
"type": "string"
},
"availability": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Costco Search (/costco-search)
**GET** `https://api.piloterr.com/v2/costco/search`
Costco Search API: search product listings by keyword or URL across 13 regional Costco sites. Returns prices, availability, review counts and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full regional search URL. **Examples:** ``` home ``` ``` home + region=fr ``` ``` https://www.costco.co.uk/search?text=sonos ``` ``` https://www.costco.ca/CatalogSearch?keyword=home¤tPage=2 ``` Full URLs auto-detect region and pagination format. Use **Costco Product** on any result `url` for PDP details. |
| `region` | query | `string` | No | Regional Costco site. Accepts a region code (`us`, `fr`, `ca`, `uk`, …) or full host (`www.costco.fr`). Defaults to US (`www.costco.com`) when omitted. **Examples:** ``` fr ``` ``` ca ``` ``` www.costco.co.uk ``` |
| `page` | query | `number` | No | 1-based page number override. Uses `currentPage` (most regions) or `page` (UK) when rebuilding pagination URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"region": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"availability": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"region": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase Company Info (/crunchbase-company-info)
**GET** `https://api.piloterr.com/v2/crunchbase/company/info`
Convert URLs into detailed Crunchbase profiles for immediate company insights.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | No | Search for a company by its name, permalink, or full Crunchbase URL. - `openai`: Search by company name or slug - `e8faa410-c307-482b-a389-74c6507ed242`: Search by UUID - `https://www.crunchbase.com/organization/openai`: Full Crunchbase organization URL **Note:** Mutually exclusive with `domain`: use one or the other, never both. **Example:** openai |
| `domain` | query | `string` | No | Perform a reverse lookup by domain name to find the associated Crunchbase company profile. **Accepted formats:** - `piloterr.com`: Plain domain name - `https://piloterr.com`: Full URL with protocol **Note:** Mutually exclusive with `query`: use one or the other, never both. **Example:** piloterr.com |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"uuid": {
"type": "string"
},
"aliases": {
"type": "array",
"items": {
"type": "string"
}
},
"founded": {
"type": "string"
},
"website": {
"type": "string"
},
"headline": {
"type": "string"
},
"location": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
},
"permalink": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
},
"description": {
"type": "string"
},
"company_type": {
"type": "string"
},
"phone_number": {
"type": "string"
},
"employee_count": {
"type": "string"
},
"semrush_summary": {
"type": "object",
"properties": {
"semrush_global_rank": {
"type": "integer"
},
"semrush_visits_latest_month": {
"type": "integer"
}
}
},
"social_networks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"operating_status": {
"type": "string"
},
"funding_rounds_headline": {
"type": "object",
"properties": {
"funding_total": {
"type": "object",
"properties": {
"value": {
"type": "integer"
},
"currency": {
"type": "string"
},
"value_usd": {
"type": "integer"
}
}
},
"num_funding_rounds": {
"type": "integer"
}
}
},
"company_financials_highlights": {
"type": "object",
"properties": {
"num_exits": {
"type": "integer"
},
"num_funds": {
"type": "integer"
},
"funding_total": {
"type": "object",
"properties": {
"value": {
"type": "integer"
},
"currency": {
"type": "string"
},
"value_usd": {
"type": "integer"
}
}
},
"num_investors": {
"type": "integer"
},
"num_investments": {
"type": "integer"
},
"num_funding_rounds": {
"type": "integer"
},
"num_lead_investors": {
"type": "integer"
},
"num_lead_investments": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase Event (/crunchbase-event)
**GET** `https://api.piloterr.com/v2/crunchbase/event`
Retrieve detailed Crunchbase event profiles with speakers, sponsors, exhibitors, and press timelines.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Retrieve event data using its Crunchbase permalink or full URL. - `london-tech-week-2025`: Event permalink slug - `https://www.crunchbase.com/event/london-tech-week-2025`: Full Crunchbase event URL **Note:** The permalink is the last segment of the Crunchbase event URL. **Example:** london-tech-week-2025 |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"title": {
"type": "string"
},
"ends_on": {
"type": "string"
},
"speakers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"job_title": {
"type": "string"
},
"permalink": {
"type": "string"
},
"organization": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
}
}
},
"sponsors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
},
"short_description": {
"type": "string"
}
}
}
},
"timeline": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"date": {
"type": "string"
},
"type": {
"type": "string"
},
"uuid": {
"type": "string"
},
"title": {
"type": "string"
},
"publisher": {
"type": "string"
}
}
}
}
}
},
"is_locked": {
"type": "boolean"
},
"starts_on": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
},
"event_type": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"num_speakers": {
"type": "integer"
},
"num_sponsors": {
"type": "integer"
},
"num_exhibitors": {
"type": "integer"
},
"num_contestants": {
"type": "integer"
},
"registration_url": {
"type": "string"
},
"short_description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase Funding Round (/crunchbase-funding-round)
**GET** `https://api.piloterr.com/v2/crunchbase/funding_round`
Retrieve full details of a specific Crunchbase funding round including investors and press timeline.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The permalink of the specific funding round to retrieve. This is the unique identifier found in the Crunchbase funding round URL. **How to find the permalink:** Navigate to the funding round page on Crunchbase: the permalink is the last path segment of the URL. **Example:** openai-secondary-market--3bab6e98 **Notes:** - Double dashes (`--`) separate the organization name from the round UUID suffix. - You can retrieve funding round permalinks from the `crunchbase/funding_rounds` endpoint. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"cards": {
"type": "object",
"properties": {
"timeline": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"entities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"properties": {
"type": "object",
"properties": {
"identifier": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
},
"activity_date": {
"type": "string"
},
"entity_def_id": {
"type": "string"
},
"activity_entities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
},
"investors_list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"identifier": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
},
"investor_identifier": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
}
}
}
},
"investors_headline": {
"type": "object",
"properties": {
"num_investors": {
"type": "integer"
}
}
},
"lead_investors_image_list": {
"type": "array",
"items": {}
}
}
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
},
"num_investors": {
"type": "integer"
},
"overview_fields_v2": {
"type": "object",
"properties": {
"investment_type": {
"type": "string"
},
"preview_properties": {
"type": "object",
"properties": {
"announced_on": {
"type": "object",
"properties": {
"is_present": {
"type": "boolean"
}
}
},
"money_raised": {
"type": "object",
"properties": {
"is_present": {
"type": "boolean"
}
}
}
}
},
"funded_organization_identifier": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase Funding Rounds (/crunchbase-funding-rounds)
**GET** `https://api.piloterr.com/v2/crunchbase/funding_rounds`
Monitor funding history and financial milestones of companies with Crunchbase data.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `days_since_announcement` | query | `number` | No | Filter funding rounds announced within the last N days. **Note:** The response returns a maximum of **15 results per request**. If `total` exceeds 15, combine with `investment_type` filters to paginate across rounds. **Example:** 30 |
| `investment_type` | query | `string` | No | Filter by funding round type. Accepted values: - `pre_seed`, `seed`, `series_a`, `series_b`, `series_c`, `series_d`, `series_e`, `series_f`, `series_g`, `series_h`, `series_i`, `series_j`, `series_unknown`, `angel`, `private_equity`, `debt_financing`, `convertible_note`, `grant`, `corporate_round`, `equity_crowdfunding`, `product_crowdfunding`, `secondary_market`, `post_ipo_equity`, `post_ipo_debt`, `post_ipo_secondary`, `non_equity_assistance`, `initial_coin_offering`, `undisclosed` **Example:** seed |
| `investor_identifiers` | query | `string` | No | Filter rounds by a specific investor using their Crunchbase UUID. **How to find an investor UUID:** Use the `crunchbase/company/info` or `crunchbase/people/info` endpoints to retrieve the `uuid` of the target investor. **Example:** df14597c-3e51-8d58-9c15-fb3de3a34a54 |
| `funded_organization_identifier` | query | `string` | No | Filter rounds by a specific funded organization using their Crunchbase UUID. **How to find an organization UUID:** Use the `crunchbase/company/info` endpoint and read the `uuid` field from the response. **Example:** cf2c678c-b81a-80c3-10d1-9c5e76448e51 |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"identifier": {
"type": "object",
"properties": {
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
},
"investment_type": {
"type": "string"
},
"investor_identifiers": {
"type": "array",
"items": {}
},
"funded_organization_identifier": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"uuid": {
"type": "string"
},
"value": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"entity_def_id": {
"type": "string"
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase People Info (/crunchbase-people-info)
**GET** `https://api.piloterr.com/v2/crunchbase/people/info`
Retrieve detailed Crunchbase profiles for individuals including career history, investments, and board positions.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search for a person by their name, Crunchbase permalink, or full Crunchbase profile URL. - `elon-musk`: Search by permalink slug - `https://www.crunchbase.com/person/elon-musk`: Full Crunchbase person URL - `Sam Altman`: Search by full name **Example:** elon-musk |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"title": {
"type": "string"
},
"gender": {
"type": "string"
},
"image_id": {
"type": "string"
},
"education": {
"type": "array",
"items": {
"type": "object",
"properties": {
"school": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
},
"started_on": {
"nullable": true
},
"degree_name": {
"nullable": true
},
"completed_on": {
"nullable": true
},
"education_uuid": {
"type": "string"
}
}
}
},
"facet_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"locations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
},
"permalink": {
"type": "string"
},
"investments": {
"type": "array",
"items": {}
},
"past_positions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"ended_on": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"precision": {
"type": "string"
}
}
},
"job_uuid": {
"type": "string"
},
"started_on": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"precision": {
"type": "string"
}
}
},
"organization": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
}
}
},
"social_networks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"num_current_jobs": {
"type": "integer"
},
"current_positions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"job_uuid": {
"type": "string"
},
"organization": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"uuid": {
"type": "string"
},
"permalink": {
"type": "string"
}
}
}
}
}
},
"short_description": {
"type": "string"
},
"rank_principal_investor": {
"type": "integer"
},
"num_founded_organizations": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Crunchbase Search (/crunchbase-search)
**GET** `https://api.piloterr.com/v2/crunchbase/search`
Search Crunchbase for companies and people by keyword and retrieve their profiles and Crunchbase identifiers.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The search keyword or phrase to query across Crunchbase. Searches both organizations and people. **Examples:** - `openai`: Search by company name - `elon`: Search by person or company name - `fintech london`: Multi-word search **Note:** The response includes both `organization` and `person` type results. Use the `type` field in each result to distinguish them. **Example:** openai |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"uuid": {
"type": "string"
},
"image_id": {
"type": "string"
},
"permalink": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Dataset Linkedin Company (/datasets-linkedin-company)
**POST** `https://api.piloterr.com/v2/datasets/linkedin/company`
Query the LinkedIn company dataset with Elasticsearch Query DSL. Filter by name, domain, industry, headcount, and country. POST-only, 1 credit per row returned.
**Credit cost:** 1 credit per row
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"hits": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"_type": {
"type": "string"
},
"_index": {
"type": "string"
},
"_score": {
"type": "number"
},
"_source": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"founded": {
"type": "integer"
},
"tagline": {
"type": "string"
},
"website": {
"type": "string"
},
"industry": {
"type": "string"
},
"created_at": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"description": {
"type": "string"
},
"staff_count": {
"type": "integer"
},
"staff_range": {
"type": "string"
},
"linkedin_url": {
"type": "string"
},
"locations_list": {
"type": "array",
"items": {}
},
"last_crawled_at": {
"type": "string"
},
"headquarter_city": {
"nullable": true
},
"headquarter_line1": {
"nullable": true
},
"linkedin_id_alpha": {
"type": "string"
},
"specialities_list": {
"type": "array",
"items": {
"type": "string"
}
},
"headquarter_country": {
"nullable": true
},
"linkedin_id_numeric": {
"type": "integer"
},
"headquarter_postal_code": {
"nullable": true
}
}
}
}
}
},
"total": {
"type": "integer"
},
"max_score": {
"type": "number"
},
"search_metadata": {
"type": "object",
"properties": {
"credits": {
"type": "object",
"properties": {
"linkedin_company_count": {
"type": "integer"
}
}
},
"account_id": {
"type": "string"
},
"account_name": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# DigiKey Product (/digikey-product)
**GET** `https://api.piloterr.com/v2/digikey/product`
Get a DigiKey product page: part numbers, manufacturer, price tiers, stock, images, datasheet and specs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full DigiKey product detail URL, or relative path. **Examples:** ``` https://www.digikey.fr/fr/products/detail/phoenix-contact/2981965/3604293 ``` ``` /fr/products/detail/excel-blades/50500/24758728 ``` Relative paths require `region`. |
| `region` | query | `string` | No | DigiKey country TLD when `query` is a relative path. **Examples:** `fr`, `com`, `de`, `co.uk` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"stock": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"locale": {
"type": "string"
},
"region": {
"type": "string"
},
"pricing": {
"type": "array",
"items": {
"type": "object",
"properties": {
"packaging": {
"type": "string"
},
"unit_price": {
"type": "number"
},
"break_quantity": {
"type": "integer"
},
"extended_price": {
"type": "number"
},
"digikey_part_number": {
"type": "string"
}
}
}
},
"currency": {
"type": "string"
},
"description": {
"type": "string"
},
"manufacturer": {
"type": "string"
},
"datasheet_url": {
"type": "string"
},
"is_unavailable": {
"type": "boolean"
},
"specifications": {
"type": "object",
"properties": {
"Fabricant": {
"type": "string"
},
"Conditionnement": {
"type": "string"
},
"Type d'accessoire": {
"type": "string"
},
"Statut du composant": {
"type": "string"
}
}
},
"digikey_part_number": {
"type": "string"
},
"detailed_description": {
"type": "string"
},
"manufacturer_part_number": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# DigiKey Search (/digikey-search)
**GET** `https://api.piloterr.com/v2/digikey/search`
Search DigiKey country sites by keyword or result/filter URL. Returns category branches or product listings with pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword, DigiKey result/filter URL, or relative path. **Examples:** ``` phoenix contact ``` ``` https://www.digikey.fr/fr/products/result?keywords=phoenix+contact ``` ``` https://www.digikey.fr/fr/products/filter/marteaux/246 ``` ``` /fr/products/filter/marteaux/246 ``` A full DigiKey URL auto-detects `region`. |
| `region` | query | `string` | No | DigiKey country TLD when `query` is not a full URL. **Examples:** `fr`, `com`, `de`, `co.uk`, `com.au`, `ca`, `jp` Aliases: `uk`/`gb` → `co.uk`, `au` → `com.au`. |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"title": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_count": {
"type": "integer"
},
"parent_category": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Domain DNSBL (/domain-dnsbl)
**GET** `https://api.piloterr.com/v2/domain/dnsbl`
Check if a domain or IP address is listed in DNS-based blacklists (DNSBL), with detailed provider-level detection and category information.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A domain name or IP address to check against DNS-based blacklists (DNSBL). **Examples:** 185.122.10.10 The API checks the input against hundreds of DNSBL providers and returns whether it has been blacklisted, along with the list of detecting providers and their categories. **Note:** Both IPv4 addresses and domain names are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"address": {
"type": "string"
},
"detected": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"providers": {
"type": "array",
"items": {
"type": "string"
}
},
"categories": {
"type": "object",
"properties": {
"dbl.spamhaus.org": {
"type": "array",
"items": {
"type": "string"
}
},
"peer.asn.cymru.com": {
"type": "array",
"items": {
"type": "string"
}
},
"origin.asn.cymru.com": {
"type": "array",
"items": {
"type": "string"
}
},
"spambot.bls.digibase.ca": {
"type": "array",
"items": {
"type": "string"
}
},
"abuse-contacts.abusix.org": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"providers": {
"type": "object",
"properties": {
"failed": {
"type": "array",
"items": {
"type": "string"
}
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"failed_count": {
"type": "integer"
},
"domains_count": {
"type": "integer"
}
}
},
"blacklisted": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Domain Malicious (/domain-malicious)
**GET** `https://api.piloterr.com/v2/domain/malicious`
Check if a domain or IP address has been flagged as malicious across multiple threat intelligence feeds, with source attribution.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A domain name or IP address to check for malicious activity across multiple threat intelligence sources. **Examples:** 197.33.189.70 malicious-domain.com The API cross-references the input against community-curated threat feeds and returns whether it has been flagged as malicious, along with the specific sources that reported it. **Note:** Both IPv4 addresses and domain names are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"sources": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
},
"malicious": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Domain Whois (/domain-whois)
**GET** `https://api.piloterr.com/v2/domain/whois`
Retrieve WHOIS registration data for any domain, including registration dates, registrar details, name servers, and contact information.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A domain name for which you want to retrieve WHOIS registration information. **Example:** piloterr.com The API returns full WHOIS data including domain registration dates, registrar details, name servers, and contact information (where not redacted for privacy). **Note:** Privacy protection (GDPR redaction) may hide registrant details for many domains. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"domain": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"status": {
"type": "array",
"items": {
"type": "string"
}
},
"punycode": {
"type": "string"
},
"extension": {
"type": "string"
},
"created_date": {
"type": "string"
},
"name_servers": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_date": {
"type": "string"
},
"whois_server": {
"type": "string"
},
"expiration_date": {
"type": "string"
},
"created_date_in_time": {
"type": "string"
},
"updated_date_in_time": {
"type": "string"
},
"expiration_date_in_time": {
"type": "string"
}
}
},
"registrar": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"referral_url": {
"type": "string"
}
}
},
"technical": {
"type": "object",
"properties": {
"fax": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"street": {
"type": "string"
},
"country": {
"type": "string"
},
"organization": {
"type": "string"
}
}
},
"registrant": {
"type": "object",
"properties": {
"fax": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"street": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"administrative": {
"type": "object",
"properties": {
"fax": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"country": {
"type": "string"
},
"organization": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# El Corte Inglés Product (/elcorteingles-product)
**GET** `https://api.piloterr.com/v2/elcorteingles/product`
El Corte Inglés Product API. Fetch a full product page from elcorteingles.es by URL or product id. Returns pricing, stock, images and identifiers via browser rendering.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Product URL or product id (`A…` / `MP_…`). **Examples:** ``` A200910175 ``` ``` https://www.elcorteingles.es/libros/A200910175-el-mundo-marino-nivel-3-tapa-blanda/ ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"old_price": {
"type": "number"
},
"product_id": {
"type": "string"
},
"availability": {
"type": "string"
},
"discount_percentage": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# El Corte Inglés Search (/elcorteingles-search)
**GET** `https://api.piloterr.com/v2/elcorteingles/search`
El Corte Inglés Search API. Search product listings on elcorteingles.es by keyword or URL. Returns prices, brands, images and pagination via webunlocker.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full El Corte Inglés search URL. **Examples:** ``` marino ``` ``` https://www.elcorteingles.es/search-nwx/?s=marino&stype=text_box ``` Use **El Corte Inglés Product** on any result `url` or `product_id` for full PDP data. |
| `page` | query | `number` | No | 1-based page number. Overrides `/search-nwx/{page}/` when present in the URL (default: from URL or `1`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"old_price": {
"type": "number"
},
"product_id": {
"type": "string"
},
"discount_percentage": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# E.Leclerc Product (/eleclerc-product)
**GET** `https://api.piloterr.com/v2/eleclerc/product`
Retrieve detailed product information from E.Leclerc by EAN barcode, including price, stock status, offers, and product attributes.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | E.Leclerc product EAN (European Article Number) to look up. **Example:** 3603312946485 **Notes:** - The EAN must be a valid 13-digit barcode identifier for a product listed on the E.Leclerc online store. - If no `store_id` is provided, the product is looked up on the national online store. - The response includes pricing, stock, offer details, and product attributes. |
| `store_id` | query | `string` | No | ID of the specific E.Leclerc store to fetch the product from. Get the `store_id` from the [E.Leclerc Stores](https://docs.piloterr.com/finder-eleclerc-stores) API. **Example:** 1631 **Notes:** - If omitted, the product data is fetched from the national online store. - Use a store ID to retrieve local pricing and stock for a specific physical location. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"brand": {
"nullable": true
},
"image": {
"type": "string"
},
"offer": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"is_nx_c_b": {
"type": "boolean"
},
"private_copy": {
"type": "integer"
},
"eco_contribution": {
"type": "integer"
},
"availability_status": {
"type": "string"
},
"eco_contribution_mobilier": {
"type": "integer"
},
"offer_availability_status": {
"type": "string"
}
}
},
"stimuli": {
"type": "object",
"properties": {}
},
"elected_offer": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"stock": {
"type": "integer"
},
"shop_id": {
"type": "integer"
},
"shop_name": {
"type": "string"
},
"offer_type": {
"type": "string"
},
"is_cross_dock": {
"type": "boolean"
},
"national_offer_id": {
"type": "string"
},
"is_leclerc_national_and_local": {
"type": "boolean"
},
"is_active_promotion_without_stock": {
"type": "boolean"
}
}
},
"stimuli_picto": {
"type": "object",
"properties": {}
},
"category_picto": {
"type": "string"
},
"cnc_offers_count": {
"type": "integer"
},
"other_offers_count": {
"type": "integer"
}
}
},
"title": {
"type": "string"
},
"data_layer": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"ean": {
"type": "string"
},
"name": {
"type": "string"
},
"price": {
"type": "string"
},
"vendor": {
"type": "string"
},
"section": {
"type": "string"
},
"category": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"condition": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"rating_count": {
"type": "integer"
},
"rating_average": {
"type": "number"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# E.Leclerc Search (/eleclerc-search)
**GET** `https://api.piloterr.com/v2/eleclerc/search`
Search for products on E.Leclerc by keyword with optional store-specific filtering and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or phrase to query on E.Leclerc. **Example:** pomme **Notes:** - Supports any keyword, brand name, or product category (in French). - Results are paginated; use the `page` parameter to navigate through pages. |
| `store_id` | query | `string` | No | ID of a specific E.Leclerc store to search in. Get the `store_id` from the [E.Leclerc Stores](https://docs.piloterr.com/finder-eleclerc-stores) API. **Example:** 1631 **Notes:** - If omitted, the search is performed on the national online store. - Use a store ID to get local product availability and pricing. |
| `page` | query | `number` | No | Page number for paginated results. **Notes:** - Use the `pagination.next` field in the response to determine if additional pages are available. - Increment this value to retrieve subsequent result pages. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"sku": {
"type": "string"
},
"slug": {
"type": "string"
},
"label": {
"type": "string"
},
"family": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"label": {
"type": "string"
},
"logisticClassCode": {
"type": "string"
}
}
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"sku": {
"type": "string"
},
"slug": {
"type": "string"
},
"label": {
"type": "string"
},
"offers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"shop": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
},
"signCode": {
"type": "string"
}
}
},
"stock": {
"type": "integer"
},
"locale": {
"type": "string"
},
"ranges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"price": {
"type": "integer"
},
"priceWithAllTaxes": {
"type": "integer"
}
}
},
"threshold": {
"type": "integer"
}
}
}
},
"currency": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"symbol": {
"type": "string"
}
}
},
"isDefault": {
"type": "boolean"
},
"externalId": {
"type": "string"
}
}
}
},
"offersTotal": {
"type": "integer"
}
}
}
},
"creationDate": {
"type": "string"
},
"cncOffersCount": {
"type": "integer"
},
"lastUpdateDate": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# E.Leclerc Suggest (/eleclerc-suggest)
**GET** `https://api.piloterr.com/v2/eleclerc/suggest`
Get instant product autocomplete suggestions from E.Leclerc for a given keyword or EAN.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search term or EAN to get product suggestions for. Accepted input types: - `keyword`: partial product name or category (e.g. `pomme`) - `EAN`: full product barcode (e.g. `9782330149635`) **Example:** pomme **Notes:** - Returns up to 6 autocomplete suggestions matching the query. - Each suggestion includes the product URL, slug, title, subtitle (author/brand), and image URL. - This endpoint mirrors the E.Leclerc website autocomplete search box. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"image_url": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Email Analyzes (/email-analyzes)
**GET** `https://api.piloterr.com/v2/email/analyzes`
Analyze a domain to retrieve its email infrastructure. Microsoft 365 and Google Workspace domains, SPF records, and DNS configurations.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The domain name you want to analyze for email infrastructure. **Example:** airbus.com The API will return all associated Microsoft 365 and Google Workspace domains, along with SPF records and DNS configurations. **Note:** Provide a bare domain (e.g. `airbus.com`), not a full URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"domains": {
"type": "object",
"properties": {
"airbus.com": {
"type": "array",
"items": {
"type": "string"
}
},
"apsys-airbus.com": {
"type": "array",
"items": {
"type": "string"
}
},
"airbus.onmicrosoft.com": {
"type": "array",
"items": {
"type": "string"
}
},
"globalemail.airbus.com": {
"type": "array",
"items": {}
},
"airbus.mail.onmicrosoft.com": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"is_microsoft_365": {
"type": "boolean"
},
"is_google_workspace": {
"type": "boolean"
},
"microsoft_365_domains": {
"type": "array",
"items": {
"type": "string"
}
},
"google_workspace_domains": {
"type": "array",
"items": {}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Email Finder (/email-finder)
**GET** `https://api.piloterr.com/v2/email/finder`
Find verified professional email addresses by combining a person's full name with their company domain or company name.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full name of the person whose professional email address you want to find. **Example:** Rémi Agosta **Note:** Provide the full name as accurately as possible for the best results. Common name variations are supported. |
| `company_domain` | query | `string` | No | The company's domain name. Multiple formats are accepted: - `piloterr.com`: Bare domain (preferred) - `https://www.piloterr.com`: Full URL format **Note:** At least one of `company_domain` or `company_name` is required. If both are omitted, the API returns a `400` error. |
| `company_name` | query | `string` | No | The company name of the person you are looking for. **Example:** Piloterr **Note:** At least one of `company_domain` or `company_name` is required. `company_domain` is preferred when available as it produces more accurate results. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"email": {
"type": "string"
},
"status": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"company_domain": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Email Verify (/email-verify)
**GET** `https://api.piloterr.com/v2/email/verify`
Verify whether an email address is valid and deliverable, returning a clear status to prevent bounces and maintain list hygiene.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The email address you want to verify. **Example:** remi.agosta@piloterr.com **Note:** Provide the full email address including the domain. The API checks deliverability and returns `valid`, `invalid`, or `unknown` as the status. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"email": {
"type": "string"
},
"status": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Expedia Ad (/expedia-ad)
**GET** `https://api.piloterr.com/v2/expedia/ad`
Extract structured Expedia data with the Expedia Ad Piloterr API endpoint.
**Status:** Maintenance
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | |
## Responses
### 200 Successful response
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Expedia Search (/expedia-search)
**GET** `https://api.piloterr.com/v2/expedia/search`
Execute a hotel search on Expedia using a direct search URL and receive structured hotel listings with prices, ratings, and availability.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid Expedia hotel search URL. Copy the URL directly from your Expedia search results page. **Example:** https://euro.expedia.net/Hotel-Search?currency=EUR&siteid=4400&destination=Toulouse **Notes:** - The URL must be a valid Expedia or Euro Expedia hotel search URL. - You can customize filters (dates, currency, location, guests) directly in the URL before passing it. - International Expedia domains (e.g., `euro.expedia.net`) are supported. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"location": {
"type": "string"
},
"room_info": {
"type": "string"
},
"taxes_info": {
"type": "string"
},
"description": {
"type": "string"
},
"rating_text": {
"type": "string"
},
"star_rating": {
"type": "integer"
},
"is_sponsored": {
"type": "boolean"
},
"rating_score": {
"type": "number"
},
"current_price": {
"type": "integer"
},
"reviews_count": {
"type": "integer"
},
"discount_badge": {
"nullable": true
},
"special_offers": {
"type": "array",
"items": {
"type": "string"
}
},
"taxes_included": {
"type": "boolean"
},
"rating_category": {
"type": "string"
},
"current_price_text": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Chewy Products Finder (/finder-chewy-products)
**GET** `https://api.piloterr.com/v2/finder/chewy-products`
Find Chewy product URLs by product name, ID, or any combination of filters to use in product detail and rendering API calls.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `search` | query | `string` | No | Free-text search across product titles and IDs. Optional if at least one filter is provided. **Example:** dog food |
| `query` | query | `string` | No | Legacy alias of `search`. Kept for backward compatibility. |
| `id` | query | `string` | No | Filter by one or several internal product IDs. Provide a comma-separated list to match multiple IDs. **Example:** 312253,312254 |
| `domain` | query | `string` | No | Filter by website domain. Comma-separated values are accepted. **Example:** com |
| `filter` | query | `string` | No | Raw filter expression for advanced filtering. Combined with the shorthand filters using `AND`. **Example:** domain = "com" AND title CONTAINS "puppy" |
| `limit` | query | `number` | No | Maximum number of results to return. Default is 10, maximum is 100. |
| `offset` | query | `number` | No | Number of results to skip for pagination. Default is 0. |
| `sort` | query | `string` | No | Sort the results. Provide one or several attributes with `:asc` or `:desc`, comma-separated. **Example:** title:asc |
| `fields` | query | `string` | No | Comma-separated list of fields to return. Defaults to all fields. **Example:** id,title,url |
| `count` | query | `boolean` | No | When `true`, the response is wrapped in an object containing the total estimated count plus the hits: `{ count, hits }`. When `false` or omitted, returns the raw hits array. **Example:** true |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Eleclerc Stores Finder (/finder-eleclerc-stores)
**GET** `https://api.piloterr.com/v2/finder/eleclerc-stores`
Search Eleclerc stores by name or city to retrieve contact details, opening hours, and geolocation.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Filters stores based on a search string. The search is **non-case-sensitive** and returns all stores whose names contain the specified string. **Example:** paris |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"lines": {
"type": "array",
"items": {
"type": "string"
}
},
"zipcode": {
"type": "string"
},
"country_code": {
"nullable": true
}
}
},
"contact": {
"type": "object",
"properties": {
"email": {
"nullable": true
},
"phone": {
"type": "string"
}
}
},
"geometry": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"store_id": {
"type": "string"
},
"opening_hours": {
"type": "object",
"properties": {
"usual": {
"type": "object",
"properties": {
"1": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"2": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"3": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"4": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"5": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"6": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"7": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
}
}
},
"timezone": {
"type": "string"
}
}
},
"weekly_opening": {
"type": "object",
"properties": {
"1": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"2": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"3": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"4": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"5": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"6": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"7": {
"type": "object",
"properties": {
"hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "string"
},
"start": {
"type": "string"
}
}
}
},
"isSpecial": {
"type": "boolean"
}
}
},
"timezone": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Countries Finder (/finder-google-countries)
**GET** `https://api.piloterr.com/v2/finder/google-countries`
Search the list of Google-supported countries by name or country code to use in Google Search API requests.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Filters countries based on a search string. The search is **non-case-sensitive** and returns all countries whose names contain the specified string. **Example:** fr |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"country_code": {
"type": "string"
},
"country_name": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Languages Finder (/finder-google-languages)
**GET** `https://api.piloterr.com/v2/finder/google-languages`
Search the list of Google-supported languages by name to retrieve language codes for use in Google Search API requests.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Filters languages based on a search string. The search is **non-case-sensitive** and returns all languages whose names contain the specified string. **Example:** french |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"language_code": {
"type": "string"
},
"language_name": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Locations Finder (/finder-google-locations)
**GET** `https://api.piloterr.com/v2/finder/google-locations`
Search Google-supported locations by name to retrieve location IDs and GPS coordinates for geo-targeted search API calls.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Restricts your search to locations that contain the supplied string. **Example:** Paris **Note:** Searching 'Austin' will find 'Austin, TX', 'The University of Texas at Austin', 'Rochester, MN-Mason City, IA-Austin, MN, United States', etc. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"gps": {
"type": "array",
"items": {
"type": "number"
}
},
"name": {
"type": "string"
},
"reach": {
"type": "integer"
},
"google_id": {
"type": "integer"
},
"target_type": {
"type": "string"
},
"country_code": {
"type": "string"
},
"canonical_name": {
"type": "string"
},
"google_parent_id": {
"type": "integer"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leroy Merlin Products Finder (/finder-leroymerlin-products)
**GET** `https://api.piloterr.com/v2/finder/leroymerlin-products`
Find Leroy Merlin product URLs by product name, ID, or any combination of filters to use in product detail and rendering API calls.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `search` | query | `string` | No | Free-text search across product titles and IDs. Optional if at least one filter is provided. **Example:** perceuse |
| `query` | query | `string` | No | Legacy alias of `search`. Kept for backward compatibility. |
| `id` | query | `string` | No | Filter by one or several internal product IDs. Provide a comma-separated list to match multiple IDs. **Example:** 84827484,84827521 |
| `domain` | query | `string` | No | Filter by website domain. Comma-separated values are accepted. **Example:** fr |
| `filter` | query | `string` | No | Raw filter expression for advanced filtering. Combined with the shorthand filters using `AND`. **Example:** domain = "fr" AND title CONTAINS "festool" |
| `limit` | query | `number` | No | Maximum number of results to return. Default is 10, maximum is 100. |
| `offset` | query | `number` | No | Number of results to skip for pagination. Default is 0. |
| `sort` | query | `string` | No | Sort the results. Provide one or several attributes with `:asc` or `:desc`, comma-separated. **Example:** title:asc |
| `fields` | query | `string` | No | Comma-separated list of fields to return. Defaults to all fields. **Example:** id,title,url |
| `count` | query | `boolean` | No | When `true`, the response is wrapped in an object containing the total estimated count plus the hits: `{ count, hits }`. When `false` or omitted, returns the raw hits array. **Example:** true |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leroy Merlin Stores Finder (/finder-leroymerlin-stores)
**GET** `https://api.piloterr.com/v2/finder/leroymerlin-stores`
Search Leroy Merlin stores by name, city or postal code to retrieve their store ID, slug, address and store locator URL.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Filters Leroy Merlin stores based on a search string. The search is **non-case-sensitive** and matches store names, cities, postal codes and store slugs. **Example:** balma |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"address": {
"type": "string"
},
"store_id": {
"type": "string"
},
"store_slug": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Industries Finder (/finder-linkedin-industries)
**GET** `https://api.piloterr.com/v2/finder/linkedin-industries`
Search LinkedIn industry taxonomy to retrieve industry IDs and hierarchies for use in LinkedIn API calls and data enrichment.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Restricts your search to industries that contain the supplied string. **Example:** software development |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"label": {
"type": "string"
},
"level": {
"type": "string"
},
"hierarchie": {
"type": "string"
},
"description": {
"type": "string"
},
"industry_id": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mr.Bricolage Stores Finder (/finder-mrbricolage-stores)
**GET** `https://api.piloterr.com/v2/finder/mrbricolage-stores`
Search Mr.Bricolage stores by name, city or postal code to retrieve their store ID, slug, contact information and geolocation.
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Filters Mr.Bricolage stores based on a search string. The search is **non-case-sensitive** and matches store names, cities and postal codes. **Example:** matignon |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"epj": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"region": {
"type": "string"
},
"seo_id": {
"type": "string"
},
"store_id": {
"type": "string"
},
"department": {
"type": "string"
},
"store_slug": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"country_code": {
"type": "string"
},
"magento_slug": {
"type": "string"
},
"live_page_url": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Fiverr Profile Info (/fiverr-profile-info)
**GET** `https://api.piloterr.com/v2/fiverr/profile/info`
Extract complete public profile data from any Fiverr seller, including gigs, ratings, reviews, skills, portfolio, and consultation pricing.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Fiverr profile URL of the seller you want to extract information from. **Example:** https://www.fiverr.com/likaalavidze The API returns the seller's full public profile including their bio, gigs, ratings, reviews, skills, portfolio, and consultation pricing. **Note:** Only publicly visible seller profiles are supported. The username alone (e.g. `likaalavidze`) without the full URL is not accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"country_code": {
"type": "string"
},
"country_name": {
"type": "string"
}
}
},
"profile": {
"type": "object",
"properties": {
"display_name": {
"type": "string"
}
}
},
"joined_at": {
"type": "integer"
},
"languages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"level": {
"type": "string"
}
}
}
},
"profile_image_url": {
"type": "string"
},
"is_activation_completed": {
"type": "boolean"
}
}
},
"is_pro": {
"type": "boolean"
},
"rating": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"score": {
"type": "integer"
}
}
},
"gigs_data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"gig_id": {
"type": "integer"
},
"is_pro": {
"type": "boolean"
},
"status": {
"type": "string"
},
"price_i": {
"type": "integer"
},
"seller_level": {
"type": "string"
},
"seller_rating": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"score": {
"type": "number"
}
}
}
}
}
},
"is_active": {
"type": "boolean"
},
"description": {
"type": "string"
},
"hourly_rate": {
"type": "object",
"properties": {
"price_in_cents": {
"type": "integer"
}
}
},
"seller_level": {
"type": "string"
},
"response_time": {
"type": "object",
"properties": {
"in_hours": {
"type": "integer"
}
}
},
"is_on_vacation": {
"type": "boolean"
},
"one_liner_title": {
"type": "string"
},
"achievement_level": {
"type": "string"
},
"approved_gigs_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Fiverr Search (/fiverr-search)
**GET** `https://api.piloterr.com/v2/fiverr/search`
Search Fiverr gigs using a direct search URL and receive paginated listings with seller information, gig titles, images, and seller levels.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Fiverr search URL for gigs. **Example:** https://www.fiverr.com/search/gigs?query=website+development The API returns paginated gig listings including seller information, images, gig URLs, and seller levels. **Notes:** - Use a valid Fiverr gig search URL (starting with `https://www.fiverr.com/search/gigs?query=`) - You can append additional Fiverr filters to the URL (e.g. `&filter=rating`, `&delivery_time=1`) before submitting. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"type": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"gig_url": {
"type": "string"
},
"position": {
"type": "integer"
},
"seller_id": {
"type": "integer"
},
"seller_name": {
"type": "string"
},
"seller_image": {
"type": "string"
},
"seller_level": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"total": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# G2 Product Info (/g2-product-info)
**GET** `https://api.piloterr.com/v2/g2/product/info`
Retrieve comprehensive product data from G2, including ratings, reviews, pricing plans, company info, and social media metrics.
**Status:** Degraded
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A product name or G2 product URL to look up. **Examples:** https://www.g2.com/products/postman Both the product slug and the full G2 product page URL are accepted. |
| `nps_score` | query | `number` | No | Filter reviews by NPS score (1–5). - `1`: 1-star reviews only - `3`: 3-star reviews only - `5`: 5-star reviews only **Note:** By default, all reviews are returned regardless of NPS score. Use this parameter to target specific sentiment segments. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"reviews": {
"type": "array",
"items": {
"type": "object",
"properties": {
"reviewer": {
"type": "object",
"properties": {
"reviewer_industry": {
"type": "string"
},
"reviewer_company_size": {
"type": "string"
}
}
},
"review_id": {
"type": "integer"
},
"publish_date": {
"type": "string"
},
"review_title": {
"type": "string"
},
"review_rating": {
"type": "integer"
}
}
}
},
"what_is": {
"type": "string"
},
"category": {
"type": "string"
},
"seller_name": {
"type": "string"
},
"twitter_url": {
"type": "string"
},
"linkedin_url": {
"type": "string"
},
"product_logo": {
"type": "string"
},
"product_name": {
"type": "string"
},
"product_type": {
"type": "string"
},
"pricing_plans": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"price": {
"type": "string"
}
}
}
},
"reviews_count": {
"type": "integer"
},
"company_location": {
"type": "string"
},
"discussions_count": {
"type": "integer"
},
"star_distribution": {
"type": "object",
"properties": {
"1": {
"type": "integer"
},
"2": {
"type": "integer"
},
"3": {
"type": "integer"
},
"4": {
"type": "integer"
},
"5": {
"type": "integer"
}
}
},
"product_description": {
"type": "string"
},
"company_year_founded": {
"type": "string"
},
"twitter_followers_count": {
"type": "integer"
},
"linkedin_employees_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# GitHub User Info (/github-user-info)
**GET** `https://api.piloterr.com/v2/github/user/info`
Retrieve public GitHub user profile data including bio, company, location, follower count, repositories, and associated email addresses.
**Status:** Degraded
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A GitHub user identifier. Multiple formats are accepted: - **GitHub profile URL:** `https://github.com/torvalds` - **Username:** `torvalds` - **Email address:** `name@company.com` **Examples:** https://github.com/torvalds **Note:** When using an email address, the lookup resolves the associated GitHub account if one is linked publicly. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"email": {
"type": "array",
"items": {
"type": "string"
}
},
"login": {
"type": "string"
},
"company": {
"type": "string"
},
"location": {
"type": "string"
},
"ssh_keys": {
"type": "string"
},
"followers": {
"type": "integer"
},
"following": {
"type": "integer"
},
"avatar_url": {
"type": "string"
},
"created_at": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"public_gists": {
"type": "string"
},
"public_repos": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Images (/google-images)
**GET** `https://api.piloterr.com/v2/google/images`
Google Images Scraper API – bringing the power of visual search to your fingertips.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The image search query to execute on Google Images. **Example:** mountain landscape |
| `gl` | query | `string` | No | Two-letter country code for the Google image search country context. - `us`: United States (default) - `fr`: France - `gb`: United Kingdom - `de`: Germany **Example:** us |
| `hl` | query | `string` | No | Two-letter language code for the Google interface language. Controls the language context of the image search. - `en`: English (default) - `fr`: French - `de`: German - `es`: Spanish **Example:** en |
| `page` | query | `number` | No | Page number for pagination. Images are returned in batches of 100 per page. - `1`: First 100 images (default) - `2`: Next 100 images - `3`: Next 100 images, etc. **Note:** To retrieve additional images, increment this parameter with the same query. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"source": {
"type": "string"
},
"position": {
"type": "integer"
},
"image_width": {
"type": "integer"
},
"image_height": {
"type": "integer"
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"gl": {
"type": "string"
},
"hl": {
"type": "string"
},
"page": {
"type": "integer"
},
"engine": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Jobs (/google-jobs)
**GET** `https://api.piloterr.com/v2/google/jobs`
Extract job listings from Google Careers for enhanced recruitment and market analysis.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Job search term or keyword for querying Google job listings. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"category": {
"type": "string"
},
"location": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google News (/google-news)
**GET** `https://api.piloterr.com/v2/google/news`
Extract real-time news articles from Google News with advanced filtering by topic, location, language, and pagination.
**Status:** Maintenance
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The news search query to execute on Google News. Supports standard Google search operators. **Examples:** - `artificial intelligence`: news about AI - `site:techcrunch.com startup`: news from a specific domain - `"climate change" 2024`: exact phrase search **Example:** artificial intelligence news |
| `location` | query | `string` | No | City or region from which the news search should originate. Simulates a search performed from the specified location. **Examples:** ``` New York, NY ``` ``` Paris, France ``` ``` Tokyo, Japan ``` |
| `uule` | query | `string` | No | Google-encoded location string for precise geographic targeting. Use this instead of `location` when you need exact control. |
| `gl` | query | `string` | No | Two-letter country code for the Google News country context. - `us`: United States - `fr`: France - `gb`: United Kingdom - `de`: Germany **Example:** us |
| `hl` | query | `string` | No | Two-letter language code for the Google News language context. - `en`: English - `fr`: French - `de`: German - `es`: Spanish **Example:** en |
| `page` | query | `number` | No | Page number of news results to retrieve. - `1`: First page (default) - `2`: Second page, etc. |
| `num` | query | `number` | No | Maximum number of results to return per page. - `10`: Default - `40`: 40 results - `100`: 100 results |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"has_next_page": {
"type": "boolean"
}
}
},
"organic_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"source": {
"type": "string"
},
"snippet": {
"type": "string"
},
"position": {
"type": "integer"
},
"displayed_link": {
"type": "string"
},
"snippet_matched": {
"type": "array",
"items": {}
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"gl": {
"type": "string"
},
"hl": {
"type": "string"
},
"num": {
"type": "integer"
},
"page": {
"type": "integer"
},
"uule": {
"nullable": true
},
"engine": {
"type": "string"
},
"location": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Search Autocomplete (/google-search-autocomplete)
**GET** `https://api.piloterr.com/v2/google/search/autocomplete`
Retrieve real-time Google Search autocomplete suggestions for any query, with support for language and country localization.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The partial or complete search query for which you want to retrieve autocomplete suggestions from Google. **Example:** coffee |
| `cp` | query | `number` | No | Cursor position in the query string where autocomplete suggestions should be generated. Determines which part of the query is being completed. - `0`: Suggestions based on the full query (default) - `5`: Suggestions based on the first 5 characters of the query |
| `gl` | query | `string` | No | Two-letter country code for the Google search country context. Defines which regional Google index is used. - `us`: United States (default) - `fr`: France - `gb`: United Kingdom - `de`: Germany **Example:** us |
| `hl` | query | `string` | No | Two-letter language code for the Google interface language. Controls the language in which suggestions are returned. - `en`: English (default) - `fr`: French - `de`: German - `es`: Spanish **Example:** en |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"suggestions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"type": "string"
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"cp": {
"type": "integer"
},
"gl": {
"type": "string"
},
"hl": {
"type": "string"
},
"engine": {
"type": "string"
},
"google_domain": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Search (/google-search)
**GET** `https://api.piloterr.com/v2/google/search`
Scrape real-time Google search results with organic listings, knowledge graph, related searches, and advanced filters.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The search query to execute on Google. Supports all standard Google search operators. **Examples:** - `best electric cars 2024`: standard keyword search - `site:linkedin.com Bill Gates`: restrict results to a specific domain - `intitle:"growth hacking" startup`: match the page title - `inurl:blog python tutorial`: match part of the URL **Example:** best electric cars 2024 |
| `tbs` | query | `string` | No | Advanced time-range and filter string for Google Search. Used to restrict results by recency. - `qdr:h`: Last hour - `qdr:d`: Last day - `qdr:w`: Last week - `qdr:m`: Last month - `qdr:y`: Last year **Example:** qdr:w |
| `location` | query | `string` | No | City or region from which the search should originate. Simulates a real user's search from the specified location. **Examples:** ``` New York, NY ``` ``` Paris, France ``` ``` Tokyo, Japan ``` |
| `uule` | query | `string` | No | Google-encoded location string for precise geographic targeting. |
| `gl` | query | `string` | No | Two-letter country code for the Google search country context. - `us`: United States - `fr`: France - `gb`: United Kingdom - `de`: Germany **Example:** us |
| `hl` | query | `string` | No | Two-letter language code for the Google interface language. - `en`: English - `fr`: French - `de`: German - `es`: Spanish **Example:** en |
| `page` | query | `number` | No | Page number of search results to retrieve. - `1`: First page (default) - `2`: Second page, etc. |
| `num` | query | `number` | No | Maximum number of results to return per page (e.g. `10`, `40`, `100`). **Note:** This parameter is deprecated and may not reliably affect the number of results returned. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"has_next_page": {
"type": "boolean"
}
}
},
"knowledge_graph": {
"nullable": true
},
"organic_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"source": {
"type": "string"
},
"snippet": {
"type": "string"
},
"position": {
"type": "integer"
},
"displayed_link": {
"type": "string"
},
"snippet_matched": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"related_searches": {
"type": "array",
"items": {
"type": "object",
"properties": {
"query": {
"type": "string"
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"gl": {
"type": "string"
},
"hl": {
"type": "string"
},
"tbs": {
"nullable": true
},
"page": {
"type": "integer"
},
"uule": {
"nullable": true
},
"engine": {
"type": "string"
},
"location": {
"nullable": true
}
}
},
"search_information": {
"type": "object",
"properties": {
"total_results": {
"nullable": true
},
"time_taken_displayed": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Google Videos (/google-videos)
**GET** `https://api.piloterr.com/v2/google/videos`
Scrape video results from Google Videos search including YouTube, Reddit, and other video platforms, with location and language support.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The video search query to execute on Google Videos. Supports standard Google search operators. **Examples:** - `paris travel guide`: travel videos about Paris - `machine learning tutorial`: educational ML videos - `site:youtube.com cooking beginner`: restrict to YouTube results **Example:** paris travel guide |
| `location` | query | `string` | No | City or region from which the video search should originate. **Examples:** ``` New York, NY ``` ``` Paris, France ``` |
| `uule` | query | `string` | No | Google-encoded location string for precise geographic targeting. |
| `gl` | query | `string` | No | Two-letter country code for the Google video search country context. - `us`: United States - `fr`: France - `gb`: United Kingdom - `de`: Germany **Example:** us |
| `hl` | query | `string` | No | Two-letter language code for the Google interface language. - `en`: English - `fr`: French - `de`: German - `es`: Spanish **Example:** en |
| `page` | query | `number` | No | Page number of video results to retrieve. - `1`: First page (default) - `2`: Second page, etc. |
| `num` | query | `number` | No | Maximum number of video results to return per page. - `10`: Default - `40`: 40 results - `100`: 100 results |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"organic_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"link": {
"type": "string"
},
"title": {
"type": "string"
},
"domain": {
"type": "string"
},
"source": {
"type": "string"
},
"channel": {
"type": "string"
},
"snippet": {
"type": "string"
},
"duration": {
"type": "string"
},
"position": {
"type": "integer"
},
"video_id": {
"type": "string"
},
"displayed_link": {
"type": "string"
},
"snippet_matched": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"search_parameters": {
"type": "object",
"properties": {
"q": {
"type": "string"
},
"gl": {
"type": "string"
},
"hl": {
"type": "string"
},
"num": {
"type": "integer"
},
"page": {
"type": "integer"
},
"uule": {
"nullable": true
},
"engine": {
"type": "string"
},
"location": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Grainger Product (/grainger-product)
**GET** `https://api.piloterr.com/v2/grainger/product`
Get a Grainger US or Canada product page: SKU, brand, price, images, description and specs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | SKU, full Grainger product URL, or relative `/product/...` path. **Examples:** ``` 3FRU1 ``` ``` https://www.grainger.com/product/PENTAIR-PENTEK-Water-Filter-Cartridge-5-micron-3FRU1 ``` ``` /product/ROCKPORT-WORKS-6-Inch-Work-Boot-Men-s-16V679 ``` |
| `region` | query | `string` | No | Grainger country when `query` is not a full URL (default: `com`). **Supported:** `com` (USD), `ca` (CAD). |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"sku": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"region": {
"type": "string"
},
"currency": {
"type": "string"
},
"product_id": {
"type": "string"
},
"description": {
"type": "string"
},
"specifications": {
"type": "object",
"properties": {
"Brand": {
"type": "string"
},
"Series": {
"type": "string"
},
"Micron Rating": {
"type": "string"
},
"Filter Media Material": {
"type": "string"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Grainger Search (/grainger-search)
**GET** `https://api.piloterr.com/v2/grainger/search`
Search Grainger US or Canada by keyword, category URL or SKU. Returns product listings, category branches and suggestions via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword, full Grainger search/category URL, category path, or SKU. **Examples:** ``` carolina ``` ``` https://www.grainger.com/search?searchQuery=carolina&searchBar=true ``` ``` https://www.grainger.com/category/abrasives ``` ``` category/abrasives ``` ``` 3FRU1 ``` A full Grainger URL auto-detects `region`. |
| `region` | query | `string` | No | Grainger country when `query` is not a full URL (default: `com`). **Supported:** - `com` → www.grainger.com, USD - `ca` → www.grainger.ca, CAD **Example:** `ca` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML as `{"result": "..."}` instead of JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"query": {
"type": "string"
},
"region": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
},
"total_results": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Homestra Ad (/homestra-ad)
**GET** `https://api.piloterr.com/v2/homestra/ad`
Retrieve detailed property listing data from Homestra, including price, size, location, features, photos, and nearby properties.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Homestra property URL or slug. **Examples:** https://homestra.com/property/charming-4-bedroom-stone-house-with-historic-towers-in-idyllic-french-village/ Both the full URL and the slug (the last part of the URL path) are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"city": {
"type": "string"
},
"size": {
"type": "integer"
},
"slug": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"garden": {
"type": "boolean"
},
"region": {
"type": "string"
},
"status": {
"type": "string"
},
"address": {
"type": "string"
},
"parking": {
"type": "boolean"
},
"basement": {
"type": "boolean"
},
"location": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"lot_size": {
"type": "integer"
},
"condition": {
"type": "string"
},
"furnished": {
"type": "boolean"
},
"created_at": {
"type": "string"
},
"country_code": {
"type": "string"
},
"swimming_pool": {
"type": "boolean"
},
"nearby_properties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
}
}
}
},
"amount_of_bedrooms": {
"type": "integer"
},
"featured_photo_url": {
"type": "string"
},
"amount_of_bathrooms": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Homestra Search (/homestra-search)
**GET** `https://api.piloterr.com/v2/homestra/search`
Search Homestra property listings using a direct search URL with filters, returning structured results with prices, sizes, locations, and photos.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid Homestra search URL with optional filters. **Example:** https://homestra.com/list/houses-for-sale/?maximum-price=900000&minimum-price=75000&page=1 You can customize filters directly in the URL, including price range, property type, location, and page number. **Notes:** - URL-encode `&` as `%26` when passing the query in a GET request parameter. - Use `page=N` to paginate through results. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"city": {
"type": "string"
},
"size": {
"type": "integer"
},
"slug": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"address": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"lot_size": {
"type": "integer"
},
"photo_url": {
"type": "string"
},
"country_code": {
"type": "string"
},
"listing_date": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Idealista Property (/idealista-property)
**GET** `https://api.piloterr.com/v2/idealista/property`
Extract full property details from an Idealista listing URL, including price, specs, photos by room, features, energy rating, and advertiser data.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Idealista property detail URL. **Example:** https://www.idealista.com/fr/inmueble/111463799/ **Notes:** - Only Idealista `/inmueble/` listing URLs are accepted. - Copy the listing URL from search results or from idealista.com. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"plans": {
"type": "array",
"items": {
"type": "string"
}
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"units": {
"type": "array",
"items": {}
},
"images": {
"type": "object",
"properties": {
"Photo": {
"type": "array",
"items": {
"type": "string"
}
},
"Salon": {
"type": "array",
"items": {
"type": "string"
}
},
"Chambre": {
"type": "array",
"items": {
"type": "string"
}
},
"Cuisine": {
"type": "array",
"items": {
"type": "string"
}
},
"Salle de bains": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"address": {
"type": "array",
"items": {
"type": "string"
}
},
"updated": {
"type": "string"
},
"currency": {
"nullable": true
},
"features": {
"type": "object",
"properties": {
"Immeuble": {
"type": "array",
"items": {
"type": "string"
}
},
"Équipement": {
"type": "array",
"items": {
"type": "string"
}
},
"Caractéristiques": {
"type": "array",
"items": {
"type": "string"
}
},
"Diagnostic de performance énergétique": {
"type": "object",
"properties": {
"emissions": {
"type": "string"
},
"consumption": {
"type": "string"
}
}
}
}
},
"location": {
"type": "string"
},
"description": {
"type": "string"
},
"advertiser_url": {
"type": "string"
},
"advertiser_name": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Idealista Search (/idealista-search)
**GET** `https://api.piloterr.com/v2/idealista/search`
Search Idealista listings from a search URL and retrieve prices, specs, photos, agency info, and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Idealista search results URL. **Example 1 (neighborhood search):** https://www.idealista.com/venta-viviendas/barcelona/ciutat-vella/la-barceloneta/ **Example 2 (city search):** https://www.idealista.com/venta-viviendas/madrid-madrid/ **Notes:** - Only Idealista search URLs are accepted. - Pagination parameters in the URL are respected. - Use each result's `link` with the Idealista Property endpoint for full listing data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "string"
}
},
"picture": {
"type": "string"
},
"currency": {
"type": "string"
},
"description": {
"type": "string"
},
"listing_company": {
"type": "string"
},
"parking_included": {
"type": "boolean"
},
"listing_company_url": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# IKEA Product (/ikea-product)
**GET** `https://api.piloterr.com/v2/ikea/product`
Extract full IKEA product details from a product URL, including price, gallery, variants, dimensions, and breadcrumb.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full IKEA product page URL. **Example:** https://www.ikea.com/fr/fr/p/gladhoejden-bureau-assis-debout-blanc-30493939/ **Notes:** - Only `/p/` product URLs on ikea.com are accepted. - Copy the listing URL from search results or from ikea.com. - Variant URLs with `-s` suffix IDs are also supported. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"measure": {
"type": "string"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"image": {
"type": "string"
}
}
}
},
"breadcrumb": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"dimensions": {
"type": "object",
"properties": {
"depth": {
"type": "string"
},
"width": {
"type": "string"
},
"height": {
"type": "string"
}
}
},
"description": {
"type": "string"
},
"price_amount": {
"type": "integer"
},
"reviews_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# IKEA Search (/ikea-search)
**GET** `https://api.piloterr.com/v2/ikea/search`
Search IKEA products from a search URL and retrieve prices, ratings, dimensions, images, and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full IKEA search URL. **Example 1 (French search):** https://www.ikea.com/fr/fr/search/?q=meuble **Example 2 (page 2):** https://www.ikea.com/fr/fr/search/?q=meuble&page=2 **Example 3 (UK search):** https://www.ikea.com/gb/en/search/?q=desk **Notes:** - Only `ikea.com` `/search/` URLs with a `q` parameter are accepted. - Copy the full URL from ikea.com, including country and language segments (`/fr/fr/`, `/gb/en/`, etc.). - Pagination via `?page=2` in the URL. - Use each result's `url` with the IKEA Product endpoint for full product data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"measure": {
"type": "string"
},
"currency": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"price_amount": {
"type": "number"
},
"reviews_count": {
"type": "integer"
},
"online_sellable": {
"type": "boolean"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"next_url": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Immowelt Property (/immowelt-property)
**GET** `https://api.piloterr.com/v2/immowelt/property`
Extract full property details from an Immowelt listing URL, including price, specs, photo gallery, energy certificate, location, and contact data.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Immowelt property detail URL. **Example:** https://www.immowelt.de/expose/65de0309-bf41-48ce-86fd-6a3441fd0487 **Notes:** - Only `/expose/` listing URLs are accepted. - Copy the listing URL from search results or from immowelt.de. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"tags": {
"type": "object",
"properties": {
"is_new": {
"type": "boolean"
},
"has3_d_visit": {
"type": "boolean"
},
"is_exclusive": {
"type": "boolean"
},
"has_brokerage_fee": {
"type": "boolean"
}
}
},
"brand": {
"type": "string"
},
"sections": {
"type": "object",
"properties": {
"key": {
"type": "object",
"properties": {
"keys": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
},
"price": {
"type": "object",
"properties": {
"base": {
"type": "object",
"properties": {
"main": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "object",
"properties": {
"main": {
"type": "string"
}
}
},
"value": {
"type": "object",
"properties": {
"alt": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
},
"main": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
}
}
}
},
"type": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "object",
"properties": {
"main": {
"type": "string"
}
}
},
"value": {
"type": "object",
"properties": {
"main": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"layout": {
"type": "string"
},
"breakdown": {
"type": "object",
"properties": {
"bar": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"color_type": {
"type": "string"
},
"percentage": {
"type": "number"
}
}
}
},
"total": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"is_estimated": {
"type": "boolean"
}
}
},
"groups": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "object",
"properties": {
"main": {
"type": "string"
}
}
},
"value": {
"type": "object",
"properties": {
"alt": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
},
"main": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
}
},
"color_type": {
"type": "string"
},
"highlighted": {
"type": "boolean"
}
}
}
}
}
},
"additional": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"label": {
"type": "string"
}
}
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"type": {
"type": "string"
},
"units": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"main": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "object",
"properties": {
"main": {
"type": "string"
}
}
},
"value": {
"type": "object",
"properties": {
"main": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
}
},
"highlighted": {
"type": "boolean"
}
}
}
}
},
"details": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"prices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "object",
"properties": {
"main": {
"type": "string"
}
}
},
"value": {
"type": "object",
"properties": {
"alt": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
},
"main": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"energy": {
"type": "object",
"properties": {
"features": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"has_scales": {
"type": "boolean"
},
"certificates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"scales": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"type": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"segments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"aria_label": {
"type": "string"
},
"efficiency_class": {
"type": "object",
"properties": {
"index": {
"type": "integer"
},
"rating": {
"type": "string"
}
}
}
}
}
},
"features": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
}
}
},
"gallery": {
"type": "object",
"properties": {
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"key": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
},
"features": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"elements": {
"type": "array",
"items": {
"type": "object",
"properties": {
"icon": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
},
"additional_text": {
"type": "string"
}
}
},
"preview": {
"type": "array",
"items": {
"type": "object",
"properties": {
"icon": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
},
"location": {
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"street": {
"type": "string"
},
"country": {
"type": "string"
},
"district": {
"type": "string"
},
"zip_code": {
"type": "string"
}
}
},
"geometry": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"is_address_published": {
"type": "boolean"
}
}
},
"mortgage": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"prov": {
"type": "integer"
},
"brand": {
"type": "string"
},
"price": {
"type": "integer"
},
"region": {
"type": "string"
},
"country": {
"type": "string"
},
"monthly_amount": {
"type": "integer"
},
"show_rent_label": {
"type": "boolean"
},
"federal_state_geo_id": {
"type": "string"
},
"show_monthly_rate_label": {
"type": "boolean"
},
"formatted_monthly_amount": {
"type": "string"
}
}
},
"documents": {
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"subtitle": {
"type": "string"
}
}
}
},
"display": {
"type": "boolean"
}
}
},
"hard_facts": {
"type": "object",
"properties": {
"facts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"split_value": {
"type": "string"
}
}
}
},
"price": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"addition": {
"type": "object",
"properties": {
"value": {
"type": "string"
}
}
},
"formatted": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"financial_link": {
"type": "object",
"properties": {
"href": {
"type": "string"
},
"label": {
"type": "string"
},
"partner_name": {
"type": "string"
}
}
},
"additional_information": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"prices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
},
"keyfacts": {
"type": "array",
"items": {
"type": "string"
}
},
"location_description": {
"type": "string"
}
}
},
"partner_ad": {
"type": "object",
"properties": {
"show_partner_ad": {
"type": "boolean"
}
}
},
"description": {
"type": "object",
"properties": {
"texts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
}
}
},
"headline": {
"type": "string"
},
"description": {
"type": "string"
}
}
},
"enriched_media": {
"type": "object",
"properties": {
"medias": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"url_type": {
"type": "string"
},
"preview_image": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"key": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"description": {
"type": "string"
}
}
},
"virtual_tours": {
"type": "object",
"properties": {
"provider": {
"type": "string"
},
"policy_value": {
"type": "string"
}
}
}
}
}
}
}
},
"area_description": {
"type": "object",
"properties": {
"headline": {
"type": "string"
},
"metadata": {
"type": "object",
"properties": {
"language": {
"type": "string"
}
}
},
"description": {
"type": "string"
}
}
},
"main_description": {
"type": "object",
"properties": {
"headline": {
"type": "string"
},
"metadata": {
"type": "object",
"properties": {
"language": {
"type": "string"
}
}
},
"description": {
"type": "string"
}
}
},
"price_comparison": {
"type": "object",
"properties": {
"is_sale": {
"type": "boolean"
},
"legal_text": {
"type": "string"
},
"price_per_sqm": {
"type": "string"
},
"has_main_price": {
"type": "boolean"
}
}
},
"seller_lead_link": {
"type": "object",
"properties": {}
},
"extended_info_description": {
"type": "object",
"properties": {
"headline": {
"type": "string"
},
"metadata": {
"type": "object",
"properties": {
"language": {
"type": "string"
}
}
},
"description": {
"type": "string"
}
}
}
}
},
"contact_sections": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"brand": {
"type": "string"
},
"static": {
"type": "object",
"properties": {
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"sticky": {
"type": "object",
"properties": {
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"provider": {
"type": "object",
"properties": {
"badges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"duration": {
"type": "string"
},
"image_url": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
},
"address": {
"type": "string"
},
"website": {
"type": "string"
},
"imprint_url": {
"type": "string"
},
"profile_url": {
"type": "string"
},
"contact_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"display": {
"type": "boolean"
},
"logo_url": {
"nullable": true
},
"subtitle": {
"type": "string"
}
}
},
"display_links": {
"type": "boolean"
},
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
},
"publisher_type": {
"type": "string"
},
"is_private_owner": {
"type": "boolean"
},
"intermediary_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"display": {
"type": "boolean"
},
"logo_url": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"logo_href": {
"type": "string"
}
}
},
"agency_legal_informations": {
"type": "array",
"items": {}
}
}
},
"agency_id": {
"type": "string"
},
"legacy_id": {
"type": "string"
},
"contact_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"agency_logo": {
"type": "object",
"properties": {
"display": {
"type": "boolean"
},
"logo_url": {
"type": "string"
}
}
},
"contact_logo": {
"type": "object",
"properties": {
"display": {
"type": "boolean"
},
"logo_url": {
"nullable": true
}
}
},
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
},
"is_private_owner": {
"type": "boolean"
}
}
},
"contact_form": {
"type": "object",
"properties": {
"contact_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
},
"is_private_owner": {
"type": "boolean"
}
}
},
"custom_fields": {
"type": "object",
"properties": {
"location_enabled": {
"type": "boolean"
}
}
},
"subject_label": {
"type": "string"
},
"display_owner_flag": {
"type": "boolean"
},
"should_send_lead_value": {
"type": "boolean"
}
}
},
"mobile_phones": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"logo_url": {
"nullable": true
},
"subtitle": {
"type": "string"
},
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
},
"is_private_owner": {
"type": "boolean"
}
}
},
"confirmation_page": {
"type": "object",
"properties": {
"is_finance_offer_button_enabled": {
"type": "boolean"
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Immowelt Search (/immowelt-search)
**GET** `https://api.piloterr.com/v2/immowelt/search`
Search Immowelt listings from a search URL and retrieve prices, specs, photos, agency info, and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Immowelt search URL. **Example 1 (rental search):** https://www.immowelt.de/suche/mieten/wohnung/deutschland/ad02de1 **Example 2 (legacy liste URL):** https://www.immowelt.de/liste/berlin/wohnungen/mieten **Example 3 (page 2):** https://www.immowelt.de/suche/mieten/wohnung/deutschland/ad02de1?page=2 **Notes:** - Supports `/suche/` and `/liste/` search URLs copied from immowelt.de. - Pagination via `?page=2` in the URL. - Use each result's `url` with the Immowelt Property endpoint for full listing data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"cps": {
"type": "string"
},
"url": {
"type": "string"
},
"tags": {
"type": "object",
"properties": {
"is_new": {
"type": "boolean"
},
"has3_d_visit": {
"type": "boolean"
},
"is_exclusive": {
"type": "boolean"
},
"has_brokerage_fee": {
"type": "boolean"
}
}
},
"type": {
"type": "string"
},
"brand": {
"type": "string"
},
"portal": {
"type": "string"
},
"status": {
"type": "string"
},
"display": {
"type": "string"
},
"gallery": {
"type": "object",
"properties": {
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"key": {
"type": "string"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
},
"location": {
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"street": {
"type": "string"
},
"country": {
"type": "string"
},
"zip_code": {
"type": "string"
}
}
},
"is_address_published": {
"type": "boolean"
}
}
},
"metadata": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"status": {
"type": "object",
"properties": {
"status": {
"type": "boolean"
},
"enrichments": {
"type": "object",
"properties": {
"geo": {
"type": "boolean"
},
"media": {
"type": "boolean"
},
"online_id": {
"type": "boolean"
},
"intermediary": {
"type": "boolean"
},
"contact_settings": {
"type": "boolean"
}
}
}
}
},
"legacy_id": {
"type": "string"
},
"update_date": {
"type": "string"
},
"creation_date": {
"type": "string"
}
}
},
"provider": {
"type": "object",
"properties": {
"badge": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"image_url": {
"type": "string"
}
}
},
"address": {
"type": "string"
},
"website": {
"type": "string"
},
"imprint_url": {
"type": "string"
},
"profile_url": {
"type": "string"
},
"agency_strip": {
"type": "object",
"properties": {
"font_color": {
"type": "string"
},
"agency_color": {
"type": "string"
}
}
},
"contact_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"display": {
"type": "boolean"
},
"logo_url": {
"nullable": true
},
"subtitle": {
"type": "string"
}
}
},
"display_links": {
"type": "boolean"
},
"phone_numbers": {
"type": "array",
"items": {
"type": "string"
}
},
"publisher_type": {
"type": "string"
},
"is_private_owner": {
"type": "boolean"
},
"intermediary_card": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"display": {
"type": "boolean"
},
"logo_url": {
"type": "string"
},
"subtitle": {
"type": "string"
},
"logo_href": {
"type": "string"
}
}
},
"agency_legal_informations": {
"type": "array",
"items": {}
}
}
},
"raw_data": {
"type": "object",
"properties": {
"price": {
"type": "integer"
},
"nbroom": {
"type": "integer"
},
"surface": {
"type": "object",
"properties": {
"main": {
"type": "integer"
},
"plot": {
"nullable": true
}
}
},
"providercity": {
"type": "string"
},
"property_type": {
"type": "string"
},
"providerzipcode": {
"type": "string"
},
"geo_id_hierarchy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type_key": {
"type": "string"
}
}
}
},
"distribution_type": {
"type": "string"
},
"property_sub_type": {
"type": "string"
},
"offerer_marketing_key": {
"type": "string"
},
"contact_location_enabled": {
"type": "boolean"
}
}
},
"tracking": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"city": {
"type": "string"
},
"name": {
"type": "string"
},
"price": {
"type": "integer"
},
"region": {
"type": "string"
},
"country": {
"type": "string"
},
"currency": {
"type": "string"
},
"zip_code": {
"type": "string"
},
"client_id": {
"type": "string"
},
"legacy_id": {
"type": "string"
},
"list_name": {
"type": "string"
},
"estate_type": {
"type": "string"
},
"product_type": {
"type": "string"
},
"building_state": {
"type": "string"
},
"distribution_type": {
"type": "string"
},
"energy_certificate": {
"type": "string"
}
}
},
"hard_facts": {
"type": "object",
"properties": {
"facts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"split_value": {
"type": "string"
}
}
}
},
"price": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"addition": {
"type": "object",
"properties": {
"value": {
"type": "string"
}
}
},
"formatted": {
"type": "string"
},
"aria_label": {
"type": "string"
},
"financial_link": {
"type": "object",
"properties": {
"href": {
"type": "string"
},
"label": {
"type": "string"
},
"partner_name": {
"type": "string"
}
}
},
"additional_information": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"prices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"aria_label": {
"type": "string"
}
}
}
},
"keyfacts": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"energy_class": {
"type": "string"
},
"card_provider": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"logo_url": {
"nullable": true
},
"subtitle": {
"type": "string"
},
"agency_strip": {
"type": "object",
"properties": {
"font_color": {
"type": "string"
},
"agency_color": {
"type": "string"
}
}
}
}
},
"main_description": {
"type": "object",
"properties": {
"headline": {
"type": "string"
},
"metadata": {
"type": "object",
"properties": {
"language": {
"type": "string"
}
}
},
"description": {
"type": "string"
}
}
},
"has_ai_enrichment": {
"type": "boolean"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"previous": {
"nullable": true
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Indeed Company Info (/indeed-company-info)
**GET** `https://api.piloterr.com/v2/indeed/company/info`
Indeed Company Info API. Full employer profile by slug or URL. Browser rendering returns headcount, revenue, industry and FAQ or review blocks.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Indeed company slug or full `/cmp/` URL. **Examples:** ``` Proman-1 ``` ``` https://fr.indeed.com/cmp/Proman-1 ``` Discover slugs with [Indeed Company Search](https://www.piloterr.com/library/indeed-company-search). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{\"result\": \"...\"}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"founded": {
"nullable": true
},
"revenue": {
"type": "string"
},
"website": {
"type": "string"
},
"industry": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"company_url": {
"type": "string"
},
"description": {
"type": "string"
},
"headquarter": {
"type": "string"
},
"staff_range": {
"type": "string"
},
"company_name": {
"type": "string"
},
"dynamic_sections": {
"type": "object",
"properties": {
"faq": {
"type": "object",
"properties": {
"faqs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"answer_text": {
"type": "string"
},
"answer_links": {
"type": "array",
"items": {}
},
"question_text": {
"type": "string"
},
"special_markup": {
"type": "boolean"
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Indeed Company Search (/indeed-company-search)
**GET** `https://api.piloterr.com/v2/indeed/company/search`
Indeed Company Search API. Search fr.indeed.com employer profiles by keyword or URL. Browser rendering returns ratings, industry and profile links.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Company keyword or full Indeed companies search URL. **Examples:** ``` airbus ``` ``` https://fr.indeed.com/companies/search?q=airbus ``` Use `company_url` from results with [Indeed Company Info](https://www.piloterr.com/library/indeed-company-info). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{\"result\": \"...\"}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"industry": {
"type": "string"
},
"jobs_url": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"company_url": {
"type": "string"
},
"description": {
"type": "string"
},
"reviews_url": {
"type": "string"
},
"company_name": {
"type": "string"
},
"salaries_url": {
"type": "string"
},
"reviews_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"count": {
"type": "integer"
},
"has_next": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Indeed Job Info (/indeed-job-info)
**GET** `https://api.piloterr.com/v2/indeed/job/info`
Indeed Job Info API. Fetch a full job posting by viewjob URL, sponsored click URL or job key. Browser rendering returns description and structured salary.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Indeed job URL, sponsored SERP click URL or 16-char `jk` job key. **Examples:** ``` 585181841cb618e0 ``` ``` https://fr.indeed.com/viewjob?jk=585181841cb618e0 ``` Bare `jk` values resolve on **fr.indeed.com**. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{\"result\": \"...\"}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"salary": {
"nullable": true
},
"job_key": {
"type": "string"
},
"benefits": {
"nullable": true
},
"job_type": {
"type": "string"
},
"location": {
"type": "string"
},
"company_url": {
"type": "string"
},
"description": {
"type": "string"
},
"remote_work": {
"nullable": true
},
"company_name": {
"type": "string"
},
"posted_today": {
"type": "boolean"
},
"urgently_hiring": {
"type": "boolean"
},
"remote_work_type": {
"nullable": true
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Indeed Job Salary (/indeed-job-salary)
**GET** `https://api.piloterr.com/v2/indeed/job/salary`
Indeed Job Salary API. Career salary aggregates by job title slug or URL. Browser rendering returns median, percentiles and top paying cities.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Career salary slug, full `/career/.../salaries` URL or `slug\|location` pair. **Examples:** ``` ingénieur-etudes-et-développement ``` ``` ingénieur-etudes-et-développement\|Paris-(75) ``` ``` https://fr.indeed.com/career/ing%C3%A9nieur-etudes-et-d%C3%A9veloppement/salaries ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{\"result\": \"...\"}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"period": {
"type": "string"
},
"salary": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"mean": {
"type": "number"
},
"text": {
"type": "string"
},
"type": {
"type": "string"
},
"median": {
"type": "number"
},
"num_data_points": {
"type": "integer"
}
}
},
"country": {
"type": "string"
},
"currency": {
"type": "string"
},
"location": {
"type": "string"
},
"by_period": {
"type": "object",
"properties": {
"DAILY": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"mean": {
"type": "number"
},
"type": {
"type": "string"
},
"median": {
"type": "number"
},
"num_data_points": {
"type": "integer"
}
}
},
"HOURLY": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"mean": {
"type": "number"
},
"type": {
"type": "string"
},
"median": {
"type": "number"
},
"num_data_points": {
"type": "integer"
}
}
},
"YEARLY": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"mean": {
"type": "number"
},
"type": {
"type": "string"
},
"median": {
"type": "number"
},
"num_data_points": {
"type": "integer"
}
}
},
"MONTHLY": {
"type": "object",
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"mean": {
"type": "number"
},
"type": {
"type": "string"
},
"median": {
"type": "number"
},
"num_data_points": {
"type": "integer"
}
}
}
}
},
"title_slug": {
"type": "string"
},
"location_slug": {
"nullable": true
},
"related_titles": {
"nullable": true
},
"national_salary": {
"nullable": true
},
"top_paying_cities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"salary": {
"nullable": true
},
"currency": {
"type": "string"
},
"location": {
"type": "string"
},
"location_slug": {
"type": "string"
}
}
}
},
"top_paying_companies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"salary": {
"type": "object",
"properties": {
"mean": {
"type": "number"
},
"type": {
"type": "string"
},
"count": {
"type": "integer"
}
}
},
"company_url": {
"nullable": true
},
"company_name": {
"type": "string"
},
"reviews_count": {
"type": "integer"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Indeed Job Search (/indeed-job-search)
**GET** `https://api.piloterr.com/v2/indeed/job/search`
Indeed Job Search API. Search fr.indeed.com job listings by keyword or URL. Browser rendering returns cards with salary snippets and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword, Indeed `/jobs` URL or SEO search URL on fr.indeed.com (default domain). **Examples:** ``` developpeur python ``` ``` https://fr.indeed.com/jobs?q=developpeur+python&l=Paris ``` Use `job_key` from results with [Indeed Job Info](https://www.piloterr.com/library/indeed-job-info). |
| `page` | query | `number` | No | 1-based page number (10 results per page). Default: `1`. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{\"result\": \"...\"}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"salary": {
"nullable": true
},
"job_key": {
"type": "string"
},
"location": {
"type": "string"
},
"sponsored": {
"type": "boolean"
},
"attributes": {
"type": "array",
"items": {
"type": "string"
}
},
"company_name": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"count": {
"type": "integer"
},
"has_next": {
"type": "boolean"
},
"per_page": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Instagram Post Info (/instagram-post-info)
**GET** `https://api.piloterr.com/v2/instagram/post/info`
Monitor competitors and market trends by scraping Instagram posts for valuable data.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The Instagram post or reel to retrieve. Accepts a full URL or a shortcode. **Accepted formats:** - `https://www.instagram.com/p/CshTQQlMGMl`: full post URL - `CshTQQlMGMl`: post shortcode only **Example:** https://www.instagram.com/p/CshTQQlMGMl **Notes:** - **Private accounts:** Posts from private accounts cannot be retrieved. - **Deleted posts:** Returns a not-found error if the post no longer exists. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"is_video": {
"type": "boolean"
},
"owner_id": {
"type": "string"
},
"sponsors": {
"type": "array",
"items": {}
},
"shortcode": {
"type": "string"
},
"text_lang": {
"type": "string"
},
"text_tags": {
"type": "array",
"items": {}
},
"timestamp": {
"type": "integer"
},
"like_count": {
"type": "integer"
},
"location_id": {
"type": "string"
},
"created_time": {
"type": "string"
},
"product_type": {
"nullable": true
},
"location_name": {
"type": "string"
},
"location_slug": {
"type": "string"
},
"related_posts": {
"nullable": true
},
"comments_count": {
"type": "integer"
},
"paid_partnership": {
"type": "boolean"
},
"text_tagged_users": {
"type": "array",
"items": {}
},
"video_plays_count": {
"nullable": true
},
"video_views_count": {
"nullable": true
},
"is_comments_disabled": {
"type": "boolean"
},
"attached_media_display_url": {
"type": "string"
},
"attached_media_tagged_users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
},
"attached_carousel_media_urls": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Instagram User Info (/instagram-user-info)
**GET** `https://api.piloterr.com/v2/instagram/user/info`
Gather user insights efficiently from Instagram for targeted marketing strategies.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The Instagram user to look up. Accepts a full profile URL or a username. **Accepted formats:** - `https://www.instagram.com/k.mbappe`: full profile URL - `k.mbappe`: username only **Example:** https://www.instagram.com/k.mbappe **Notes:** - **Private accounts:** Profile data is returned but posts array will be empty. - **Deactivated accounts:** Returns a not-found error. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"posts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"likes": {
"type": "integer"
},
"caption": {
"type": "string"
},
"comments": {
"type": "integer"
},
"is_video": {
"type": "boolean"
},
"shortcode": {
"type": "string"
},
"timestamp": {
"type": "integer"
},
"display_url": {
"type": "string"
}
}
}
},
"avatar": {
"type": "string"
},
"gender": {
"type": "string"
},
"private": {
"type": "boolean"
},
"website": {
"nullable": true
},
"username": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"followers": {
"type": "integer"
},
"following": {
"type": "integer"
},
"description": {
"type": "string"
},
"category_name": {
"type": "string"
},
"business_account": {
"type": "boolean"
},
"business_category_name": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Kick User Info (/kick-user-info)
**GET** `https://api.piloterr.com/v2/kick/user/info`
Retrieve complete public channel data from Kick including streamer profile, follower count, live status, and recent categories.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The Kick channel to look up. Accepts a full channel URL or a username/slug. **Accepted formats:** - `https://kick.com/xqc`: full channel URL - `xqc`: channel slug/username only **Example:** https://kick.com/xqc **Notes:** - **Banned channels:** The `is_banned` field will be `true` for banned users but data may still be returned. - **Offline streamers:** The `livestream` field will be `null` when the channel is not live. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"role": {
"nullable": true
},
"slug": {
"type": "string"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"bio": {
"type": "string"
},
"city": {
"nullable": true
},
"gender": {
"nullable": true
},
"discord": {
"type": "string"
},
"twitter": {
"type": "string"
},
"youtube": {
"type": "string"
},
"username": {
"type": "string"
},
"instagram": {
"type": "string"
},
"profile_pic": {
"type": "string"
}
}
},
"muted": {
"type": "boolean"
},
"user_id": {
"type": "integer"
},
"verified": {
"type": "boolean"
},
"is_banned": {
"type": "boolean"
},
"livestream": {
"nullable": true
},
"vod_enabled": {
"type": "boolean"
},
"banner_image": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
}
},
"playback_url": {
"type": "string"
},
"follower_badges": {
"type": "array",
"items": {}
},
"followers_count": {
"type": "integer"
},
"recent_categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"subscription_enabled": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leboncoin Ad (/leboncoin-ad)
**GET** `https://api.piloterr.com/v2/leboncoin/ad`
Extract full details from a Leboncoin ad including price, location, attributes, seller info, and images. Returns structured JSON by default, or raw HTML on demand.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Leboncoin ad ID or full ad URL. Both formats are accepted regardless of `return_page_source`. **Accepted formats:** - Ad ID: `3012186547` - Full URL: `https://www.leboncoin.fr/ad/equipement_auto/3012186547` **Examples:** ``` 3012186547 ``` ``` https://www.leboncoin.fr/ad/equipement_auto/3012186547 ``` **Note:** If the ad has been deleted or expired on Leboncoin, the endpoint returns a `404` status code. |
| `return_page_source` | query | `boolean` | No | Controls the response format. - `false` (default): returns a structured **JSON** object with all parsed ad fields (price, location, attributes, seller, images, etc.). - `true`: returns the raw **HTML** source of the ad page as a string. Useful for advanced extraction or debugging when a field is missing from the structured response. **Note:** The same `query` (ad ID or full URL) works in both modes. You don't need to change the input format depending on the value of this parameter. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"body": {
"type": "string"
},
"owner": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"siren": {
"nullable": true
},
"user_id": {
"type": "string"
}
}
},
"price": {
"type": "array",
"items": {
"type": "integer"
}
},
"images": {
"type": "object",
"properties": {
"urls": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"status": {
"type": "string"
},
"list_id": {
"type": "integer"
},
"subject": {
"type": "string"
},
"category": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
}
}
},
"location": {
"type": "object",
"properties": {
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"city": {
"type": "string"
},
"zipcode": {
"type": "string"
},
"region_id": {
"type": "string"
},
"region_name": {
"type": "string"
},
"department_id": {
"type": "string"
},
"department_name": {
"type": "string"
}
}
},
"attributes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
},
"value_label": {
"type": "string"
}
}
}
},
"price_cents": {
"type": "array",
"items": {
"type": "integer"
}
},
"creation_date": {
"type": "string"
},
"expiration_date": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leboncoin Search API (/leboncoin-search-api)
**POST** `https://api.piloterr.com/v2/leboncoin/search_api`
Search Leboncoin listings via structured POST API with advanced filters for category, location, price range, seller type, and sorting.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ads": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"body": {
"type": "string"
},
"owner": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"siren": {
"nullable": true
}
}
},
"price": {
"type": "array",
"items": {
"type": "integer"
}
},
"images": {
"type": "object",
"properties": {
"urls": {
"type": "array",
"items": {
"type": "string"
}
},
"nb_images": {
"type": "integer"
},
"small_url": {
"type": "string"
},
"thumb_url": {
"type": "string"
}
}
},
"status": {
"type": "string"
},
"ad_type": {
"type": "string"
},
"list_id": {
"type": "integer"
},
"subject": {
"type": "string"
},
"currency": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"city": {
"type": "string"
},
"zipcode": {
"type": "string"
},
"region_id": {
"type": "string"
},
"country_id": {
"type": "string"
},
"region_name": {
"type": "string"
},
"department_id": {
"type": "string"
},
"department_name": {
"type": "string"
}
}
},
"attributes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
},
"value_label": {
"type": "string"
}
}
}
},
"brand_name": {
"nullable": true
},
"category_id": {
"type": "string"
},
"price_cents": {
"type": "array",
"items": {
"type": "integer"
}
},
"category_name": {
"type": "string"
},
"creation_date": {
"type": "string"
},
"expiration_date": {
"type": "string"
}
}
}
},
"total": {
"type": "integer"
},
"max_pages": {
"type": "integer"
},
"total_all": {
"type": "integer"
},
"total_pro": {
"type": "integer"
},
"total_private": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leboncoin Search (/leboncoin-search)
**GET** `https://api.piloterr.com/v2/leboncoin/search`
Scrape Leboncoin search results or category pages by URL, returning paginated listings with price, location, and attributes. Returns structured JSON by default, or raw HTML on demand.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Leboncoin search or category URL. Both keyword searches and category browse URLs are supported. **Supported URL types:** - Category page: `https://www.leboncoin.fr/c/offres_d_emploi?locations=Paris_75015__48.84105_2.29926_2747` - Keyword search: `https://www.leboncoin.fr/recherche?text=iphone+15&category=38` - Map view: `https://www.leboncoin.fr/carte/...` **Examples:** ``` https://www.leboncoin.fr/c/offres_d_emploi?locations=Paris_75015__48.84105_2.29926_2747 ``` ``` https://www.leboncoin.fr/recherche?text=macbook&category=15 ``` **Note:** Pagination can be added by appending `&page=2` to the URL. |
| `return_page_source` | query | `boolean` | No | Controls the response format. - `false` (default): returns a structured **JSON** object with parsed search results, including the list of ads, totals, and pagination. - `true`: returns the raw **HTML** source of the search results page as a string. Useful for advanced extraction or debugging. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ads": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"body": {
"type": "string"
},
"owner": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"price": {
"type": "array",
"items": {
"type": "integer"
}
},
"images": {
"type": "object",
"properties": {
"nb_images": {
"type": "integer"
},
"small_url": {
"type": "string"
}
}
},
"status": {
"type": "string"
},
"list_id": {
"type": "integer"
},
"subject": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"zipcode": {
"type": "string"
},
"region_name": {
"type": "string"
},
"department_name": {
"type": "string"
}
}
},
"attributes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
},
"value_label": {
"type": "string"
}
}
}
},
"category_id": {
"type": "string"
},
"category_name": {
"type": "string"
},
"creation_date": {
"type": "string"
}
}
}
},
"total": {
"type": "integer"
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leroy Merlin Product (/leroymerlin-product)
**GET** `https://api.piloterr.com/v2/leroymerlin/product`
Live Leroy Merlin product detail for France and Romania from a URL or identifier: offers, currency, images, features and store context.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Leroy Merlin product URL, or an identifier (SKU / EAN / text). **Examples:** ``` https://www.leroymerlin.fr/produits/bobine-soudure-63378273.html ``` ``` https://www.leroymerlin.ro/produits/scara-aluminiu-12345678.html ``` ``` 63378273 ``` When `query` is not a URL, Piloterr searches and follows the redirect to the product page when the match is clear enough. When `query` is a full URL, `region` is detected from the domain. |
| `region` | query | `string` | No | Leroy Merlin country when `query` is a keyword or identifier (default: `fr`). Ignored when `query` is a full Leroy Merlin URL (country is detected from the domain). **Supported regions:** - `fr` (aliases: `france`, `fr-fr`) → www.leroymerlin.fr, currency `EUR` - `ro` (aliases: `romania`, `roumanie`, `ro-ro`) → www.leroymerlin.ro, currency `RON` **Examples:** ``` fr ``` ``` ro ``` ``` romania ``` |
| `store_id` | query | `string` | No | Optional store id for local offer context (prices, stock, deliveries). Must exist for the active region. Returns `400` when unknown. The response `store` block is present only when this parameter is set. **Example:** `110` |
| `return_page_source` | query | `boolean` | No | When set to `true`, returns the raw HTML of the product page instead of the parsed JSON payload. Useful for debugging or custom parsing. The response shape is `{"return": "..."}`. Defaults to `false`. **Example:** `true` |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"sku": {
"type": "string"
},
"url": {
"type": "string"
},
"store": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"region": {
"type": "string"
},
"features": {
"type": "object",
"properties": {
"Forme": {
"type": "string"
},
"Aspect": {
"type": "string"
},
"Couleur": {
"type": "string"
},
"Largeur (en cm)": {
"type": "string"
},
"Longueur (en cm)": {
"type": "string"
},
"Epaisseur (en mm)": {
"type": "string"
},
"Pays de fabrication": {
"type": "string"
},
"Destination du carrelage": {
"type": "string"
},
"Contenance de la boîte (en m²)": {
"type": "string"
},
"Nombre de carreaux dans la boîte": {
"type": "string"
}
}
},
"best_offer": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"currency": {
"type": "string"
},
"available": {
"type": "boolean"
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"time": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "number"
},
"stock": {
"type": "integer"
},
"stock_status": {
"type": "string"
}
}
}
},
"offer_type": {
"type": "string"
},
"price_excl_tax": {
"type": "number"
}
}
},
"breadcrumb": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"position": {
"type": "integer"
}
}
}
},
"description": {
"type": "string"
},
"marketing_tag": {
"type": "string"
},
"reviews_count": {
"type": "integer"
},
"total_offer_count": {
"type": "integer"
},
"sellers_composition": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Leroy Merlin Search (/leroymerlin-search)
**GET** `https://api.piloterr.com/v2/leroymerlin/search`
Live Leroy Merlin search across France and Romania: title, URL, price, currency, brand, seller, rating and pagination.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword, SKU/EAN, or full Leroy Merlin search URL. **Examples:** ``` perceuse ``` ``` bormasina ``` ``` https://www.leroymerlin.ro/search?q=drill ``` When `query` is a full URL, `region` is detected from the domain and can be omitted. |
| `region` | query | `string` | No | Leroy Merlin country when `query` is a keyword or identifier (default: `fr`). Ignored when `query` is a full Leroy Merlin URL (country is detected from the domain). **Supported regions:** - `fr` (aliases: `france`, `fr-fr`) → www.leroymerlin.fr, currency `EUR` - `ro` (aliases: `romania`, `roumanie`, `ro-ro`) → www.leroymerlin.ro, currency `RON` **Examples:** ``` fr ``` ``` ro ``` ``` romania ``` |
| `page` | query | `number` | No | 1-based page number for pagination. Defaults to `1` when omitted. Use the `pagination.total_pages` returned by a previous call to iterate. **Example:** `2` |
| `store_id` | query | `string` | No | Optional store id for local prices and availability. Must exist for the active region (`FR` stores with `region=fr`, `RO` stores with `region=ro`). Returns `400` when unknown. **Example:** `110` |
| `return_page_source` | query | `boolean` | No | When set to `true`, returns the raw HTML of the page instead of the parsed JSON payload. Useful for debugging or custom parsing. The response shape is `{"return": "..."}`. Defaults to `false`. **Example:** `true` |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"region": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"seller": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"currency": {
"type": "string"
},
"position": {
"type": "integer"
},
"is_sponsored": {
"type": "boolean"
},
"price_excl_tax": {
"type": "number"
},
"total_offer_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"has_next": {
"type": "boolean"
},
"total_pages": {
"type": "integer"
},
"current_page": {
"type": "integer"
},
"has_previous": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Lidl Product (/lidl-product)
**GET** `https://api.piloterr.com/v2/lidl/product`
Lidl Product API: fetch full product page data from a Lidl URL or ERP id. Returns pricing, images, description, availability and category via Nuxt SSR parsing.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Lidl product URL or ERP id. **Examples:** ``` 100397447 ``` ``` p100397447 ``` ``` https://www.lidl.fr/p/parkside-perceuse-a-percussion-psbm-750-b3-10-nm-750-w/p100397447 ``` |
| `region` | query | `string` | No | Target country when `query` is an ERP id only (default: `fr`). Ignored for full product URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"locale": {
"type": "string"
},
"rating": {
"type": "number"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"old_price": {
"type": "number"
},
"product_id": {
"type": "string"
},
"description": {
"type": "string"
},
"availability": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"availability_text": {
"type": "string"
},
"discount_percentage": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Lidl Search (/lidl-search)
**GET** `https://api.piloterr.com/v2/lidl/search`
Lidl Search API: search product listings by keyword or URL across Lidl online shops in 27 countries. Parses Nuxt SSR payload from HTML; returns prices, ratings, brand and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Lidl search URL. **Examples:** ``` perceuse ``` ``` https://www.lidl.de/q/search?q=bohrer ``` Use **Lidl Product** on any result `url` or `product_id` for full PDP data. |
| `region` | query | `string` | No | Target country when `query` is a keyword (default: `fr`). **Examples:** `de`, `pl`, `gb`, `nl-be`, `fr-be` Ignored when `query` is a full Lidl URL. |
| `page` | query | `number` | No | 1-based page number (default: 1). Lidl paginates by offset (`48` items per page). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"locale": {
"type": "string"
},
"rating": {
"type": "integer"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"old_price": {
"type": "number"
},
"product_id": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"discount_percentage": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Company Info (/linkedin-company-info)
**GET** `https://api.piloterr.com/v2/linkedin/company/info`
LinkedIn Company Info API. Fetch firmographics by LinkedIn slug, URL, company ID, or reverse domain lookup (`guideflow.com`). Industry, headcount, HQ, posts, and more.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | No | LinkedIn company identifier when you already know the page. **Accepted formats:** - `ferrari`: LinkedIn slug / universal name - `https://www.linkedin.com/company/ferrari`: full company URL - `7050`: numeric LinkedIn company ID - `https://www.linkedin.com/school/harvard-university/`: school page **Example:** https://www.linkedin.com/company/ferrari **Notes:** - Mutually exclusive with `domain`. - Use `domain` instead when you only have a company website. |
| `domain` | query | `string` | No | Company website domain for **reverse lookup** when you do not have the LinkedIn URL. **Accepted formats:** - `guideflow.com`: bare domain (preferred) - `https://www.guideflow.com`: full URL (domain is extracted) **Example:** guideflow.com **Notes:** - Resolves the domain to a LinkedIn company slug via indexed company data. - Mutually exclusive with `query`. - Ideal for enriching CRM records that only store `website` or `domain`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"posts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"post_id": {
"type": "string"
},
"post_url": {
"type": "string"
}
}
}
},
"founded": {
"type": "integer"
},
"tagline": {
"type": "string"
},
"website": {
"type": "string"
},
"industry": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"employees": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"profile_url": {
"type": "string"
}
}
}
},
"locations": {
"type": "array",
"items": {
"type": "string"
}
},
"company_id": {
"type": "integer"
},
"company_url": {
"type": "string"
},
"description": {
"type": "string"
},
"headquarter": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"line1": {
"type": "string"
},
"country": {
"type": "string"
},
"postal_code": {
"type": "string"
}
}
},
"staff_count": {
"type": "integer"
},
"staff_range": {
"type": "string"
},
"company_name": {
"type": "string"
},
"specialities": {
"type": "array",
"items": {
"type": "string"
}
},
"follower_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Job Count (/linkedin-job-count)
**GET** `https://api.piloterr.com/v2/linkedin/job/count`
Obtain real-time job counts on LinkedIn by industry, region, and role for strategic planning.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `keyword` | query | `string` | No | Job title or keyword to count matching postings. **Example:** Software Engineer Leave empty to count all jobs matching the other filters. |
| `experience_level` | query | `string` | No | Filter by seniority level. **Accepted values:** - `internship`: Internship positions - `entry_level`: Entry-level roles - `associate`: Associate-level roles - `mid_senior`: Mid to senior-level roles - `director`: Director-level and above |
| `job_type` | query | `string` | No | Filter by employment type. **Accepted values:** - `full_time`: Full-time positions - `part_time`: Part-time positions - `contract`: Contract roles - `temporary`: Temporary roles - `internship`: Internships - `volunteer`: Volunteer positions |
| `when` | query | `string` | No | Filter by posting recency. **Accepted values:** - `day`: Posted in the last 24 hours - `week`: Posted in the last 7 days - `month`: Posted in the last 30 days |
| `flexibility` | query | `string` | No | Filter by work arrangement. **Accepted values:** - `remote`: Fully remote positions - `flexible`: Hybrid or flexible arrangements - `on_site`: On-site only positions |
| `distance` | query | `number` | No | Maximum distance radius (in miles) from the target location (requires `geo_id`). **Accepted values:** `5`, `10`, `25`, `50`, `100` |
| `geo_id` | query | `string` | No | LinkedIn geo ID of the target location. **Example values:** - `92000000`: Worldwide (default) - `105073465`: France - `103644278`: United States Use the LinkedIn Job Suggest endpoint with `location=true` to find geo IDs for any location. |
| `company_id` | query | `string` | No | LinkedIn numeric company ID to restrict the count to jobs posted by a specific company. **Example:** - `33246798`: TikTok Use the LinkedIn Company Info endpoint to retrieve a company's ID. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Job Info (/linkedin-job-info)
**GET** `https://api.piloterr.com/v2/linkedin/job/info`
Dive into LinkedIn job data and trends for in-depth labor market insights.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The LinkedIn job posting to retrieve. Accepts a numeric job ID or a full job posting URL. **Accepted formats:** - `4390885746`: LinkedIn numeric job ID - `https://www.linkedin.com/jobs/view/4390885746`: full job URL **Example:** https://www.linkedin.com/jobs/view/4390885746 **Notes:** - **Expired listings:** Jobs that have been closed or removed will return a not-found error. - **Private jobs:** Some employer-direct postings may be restricted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"county": {
"nullable": true
},
"country": {
"type": "string"
},
"country_code": {
"type": "string"
}
}
},
"job_url": {
"type": "string"
},
"industry": {
"type": "array",
"items": {
"type": "string"
}
},
"location": {
"type": "string"
},
"functions": {
"type": "array",
"items": {
"type": "string"
}
},
"list_date": {
"type": "string"
},
"recruiter": {
"type": "object",
"properties": {
"headline": {
"type": "string"
},
"full_name": {
"type": "string"
},
"profile_url": {
"type": "string"
}
}
},
"company_url": {
"type": "string"
},
"company_logo": {
"type": "string"
},
"company_name": {
"type": "string"
},
"employment_type": {
"type": "string"
},
"job_description": {
"type": "string"
},
"seniority_level": {
"type": "string"
},
"total_applicants": {
"type": "integer"
},
"compensation_salary": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Job Search (/linkedin-job-search)
**GET** `https://api.piloterr.com/v2/linkedin/job/search`
Experience advanced job data extraction from LinkedIn, enhancing your recruitment strategy.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `keyword` | query | `string` | No | Job title or keyword to search for. **Example:** Software Engineer Leave empty to return all jobs matching the other filters. |
| `experience_level` | query | `string` | No | Filter by seniority level. **Accepted values:** - `internship`: Internship positions - `entry_level`: Entry-level roles - `associate`: Associate-level roles - `mid_senior`: Mid to senior-level roles - `director`: Director-level and above |
| `job_type` | query | `string` | No | Filter by employment type. **Accepted values:** - `full_time`: Full-time positions - `part_time`: Part-time positions - `contract`: Contract roles - `temporary`: Temporary roles - `internship`: Internships - `volunteer`: Volunteer positions |
| `when` | query | `string` | No | Filter by posting date recency. **Accepted values:** - `day`: Posted in the last 24 hours - `week`: Posted in the last 7 days - `month`: Posted in the last 30 days |
| `flexibility` | query | `string` | No | Filter by work arrangement. **Accepted values:** - `remote`: Fully remote positions - `flexible`: Hybrid or flexible arrangements - `on_site`: On-site only positions |
| `distance` | query | `number` | No | Maximum radius in miles from the target location (requires `geo_id`). **Accepted values:** `5`, `10`, `25`, `50`, `100` |
| `geo_id` | query | `string` | No | LinkedIn geo ID for the target location. Default is `92000000` (worldwide). **Example values:** - `92000000`: Worldwide - `105073465`: France - `103644278`: United States Use the LinkedIn Job Suggest endpoint with `location=true` to find geo IDs for any location. |
| `company_id` | query | `string` | No | LinkedIn numeric company ID to filter results to jobs posted by a specific company. **Example:** - `33246798`: TikTok Use the LinkedIn Company Info endpoint to retrieve a company's ID. |
| `page` | query | `number` | No | Page number for paginating through results. Default is `1`. Each page returns up to 25 job listings. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"location": {
"type": "string"
},
"list_date": {
"type": "string"
},
"company_url": {
"type": "string"
},
"company_name": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Job Suggest (/linkedin-job-suggest)
**GET** `https://api.piloterr.com/v2/linkedin/job/suggest`
Get personalized job suggestions based on keywords or locations to streamline your job search.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The keyword prefix to search for job title suggestions or location names. **Example: keyword suggestions:** Air **Example: location suggestions (when `location=true`):** Par **Notes:** - Short prefixes (2–4 characters) generally yield more results. - When `location=false`, returns job-related keyword suggestions (titles, companies, schools). - When `location=true`, returns geo locations with their `geo_id` values. |
| `location` | query | `boolean` | No | Set to `true` to receive location (geo) suggestions instead of keyword suggestions. **Accepted values:** - `false` (default): Returns job keyword suggestions (titles, companies, schools) - `true`: Returns geographic locations with their `geo_id` for use in Job Count and Job Search filters **Note:** The returned `geo_id` values can be used directly in the `geo_id` parameter of the LinkedIn Job Search and Job Count endpoints. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Post Info (/linkedin-post-info)
**GET** `https://api.piloterr.com/v2/linkedin/post/info`
Simplify LinkedIn post retrieval with our efficient API for enhanced content analysis.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The LinkedIn post to retrieve. Accepts a numeric post ID or a full post URL. **Accepted formats:** - `7027826235833520128`: LinkedIn numeric post ID - `https://www.linkedin.com/posts/tanyalindsay_...-activity-7027826235833520128-m0GW`: full post URL **Example:** https://www.linkedin.com/posts/tanyalindsay_we-have-been-building-a-web3-gaming-platform-activity-7027826235833520128-m0GW **Notes:** - **Deleted posts:** Returns a not-found error if the post has been removed. - **Comments:** Up to the first batch of comments are returned. Deep comment threads may be truncated. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"text": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"full_name": {
"type": "string"
},
"image_url": {
"nullable": true
},
"profile_type": {
"type": "string"
}
}
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"comments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"headline": {
"type": "string"
},
"full_name": {
"type": "string"
},
"image_url": {
"nullable": true
},
"profile_type": {
"type": "string"
}
}
}
}
}
},
"hashtags": {
"type": "array",
"items": {}
},
"image_url": {
"type": "string"
},
"like_count": {
"type": "integer"
},
"shared_link": {
"nullable": true
},
"shared_post": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"link": {
"nullable": true
},
"text": {
"type": "string"
},
"image": {
"nullable": true
},
"title": {
"nullable": true
}
}
},
"shared_video": {
"nullable": true
},
"comments_count": {
"type": "integer"
},
"date_published": {
"type": "string"
},
"total_engagement": {
"type": "integer"
},
"mentioned_profiles": {
"type": "array",
"items": {}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Product Info (/linkedin-product-info)
**GET** `https://api.piloterr.com/v2/linkedin/product/info`
Gain competitive insights with detailed LinkedIn product information for strategic advantage.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The full LinkedIn product page URL to retrieve. **Accepted format:** - `https://www.linkedin.com/products/15five`. LinkedIn product URL **Example:** https://www.linkedin.com/products/15five **Notes:** - Only LinkedIn product pages are supported (URLs starting with `https://www.linkedin.com/products/`). - Company pages and showcase pages are not supported by this endpoint: use LinkedIn Company Info instead. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"roles": {
"type": "array",
"items": {
"type": "string"
}
},
"logo_url": {
"type": "string"
},
"learn_more": {
"type": "string"
},
"company_url": {
"type": "string"
},
"description": {
"type": "string"
},
"company_name": {
"type": "string"
},
"product_title": {
"type": "string"
},
"background_url": {
"type": "string"
},
"product_category": {
"type": "string"
},
"similar_products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_url": {
"type": "string"
},
"company_logo": {
"type": "string"
},
"product_name": {
"type": "string"
}
}
}
},
"featured_customers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"company_url": {
"type": "string"
},
"company_logo": {
"type": "string"
},
"company_name": {
"type": "string"
}
}
}
},
"product_category_url": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# LinkedIn Profile Info (/linkedin-profile-info)
**GET** `https://api.piloterr.com/v2/linkedin/profile/info`
Streamline recruitment and business development by extracting valuable LinkedIn profile insights.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The LinkedIn profile to retrieve. Accepts a username, full profile URL, or numeric member ID. **Accepted formats:** - `williamhgates`: LinkedIn username/public identifier - `https://www.linkedin.com/in/williamhgates`: full profile URL - `ACoAAA8BYqEBmLJMezmvqCLszS5NiJtJz7L9N9M`: encoded member ID **Example:** https://www.linkedin.com/in/williamhgates **Notes:** - **Private profiles:** Some fields may be empty if the profile visibility is restricted. - **Deleted accounts:** Returns a not-found error if the account no longer exists. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"skills": {
"type": "array",
"items": {
"type": "string"
}
},
"address": {
"nullable": true
},
"summary": {
"type": "string"
},
"articles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"author": {
"type": "string"
}
}
}
},
"headline": {
"type": "string"
},
"username": {
"type": "string"
},
"full_name": {
"type": "string"
},
"languages": {
"type": "array",
"items": {}
},
"photo_url": {
"type": "string"
},
"educations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"school": {
"type": "string"
},
"end_date": {
"nullable": true
},
"end_year": {
"nullable": true
},
"start_date": {
"nullable": true
},
"start_year": {
"nullable": true
},
"field_of_study": {
"type": "string"
}
}
}
},
"experiences": {
"type": "array",
"items": {
"type": "object",
"properties": {
"company": {
"type": "string"
},
"end_date": {
"nullable": true
},
"job_title": {
"type": "string"
},
"start_date": {
"type": "string"
},
"company_url": {
"type": "string"
},
"description": {
"nullable": true
},
"company_logo": {
"type": "string"
}
}
}
},
"profile_url": {
"type": "string"
},
"certifications": {
"type": "array",
"items": {}
},
"follower_count": {
"type": "integer"
},
"recommendations": {
"type": "array",
"items": {}
},
"connection_count": {
"type": "integer"
},
"member_identifier": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Maersk Tracking (/maersk-tracking)
**GET** `https://api.piloterr.com/v2/maersk/tracking`
Track a Maersk container or BOL: origin, destination, current milestone and full transport-plan events via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Container / BOL tracking number, or full Maersk tracking URL. **Examples:** ``` MMAU1155207 ``` ``` https://www.maersk.com/tracking/MMAU1155207 ``` A bare number is resolved to `https://www.maersk.com/tracking/{number}`. Localized URLs (`/fr-fr/tracking/...`) are accepted when passed in full. |
| `return_page_source` | query | `boolean` | No | When `true`, return the rendered HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"event": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"facility": {
"type": "string"
}
}
},
"position": {
"type": "integer"
},
"is_current": {
"type": "boolean"
}
}
}
},
"origin": {
"type": "string"
},
"destination": {
"type": "string"
},
"current_event": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"event": {
"type": "string"
},
"position": {
"type": "integer"
},
"is_current": {
"type": "boolean"
}
}
},
"tracking_type": {
"type": "string"
},
"tracking_number": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# ManoMano Product Offer (/manomano-product-offer)
**GET** `https://api.piloterr.com/v2/manomano/product/offer`
Get all marketplace seller offers for a ManoMano product by product ID, including pricing, shipping, and seller information.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | ManoMano product ID. This is the numeric `product_id` returned by the ManoMano Product endpoint. **How to get this value:** 1. Call the ManoMano Product endpoint with a product URL. 2. Extract the `product_id` field from the response. 3. Use that value as the `query` parameter here. **Example:** 28910102 **Note:** Do not pass a product URL here, only numeric product IDs are accepted. |
| `region` | query | `string` | Yes | Region code for the ManoMano platform. Must match the original product's domain. - `FR`: France (manomano.fr) - `GB`: United Kingdom (manomano.co.uk) - `DE`: Germany (manomano.de) - `ES`: Spain (manomano.es) - `IT`: Italy (manomano.it) **Example:** FR |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"master_product": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"article_id": {
"type": "string"
}
}
}
}
},
"market": {
"type": "string"
},
"seller": {
"type": "object",
"properties": {
"is_m_f": {
"type": "boolean"
},
"contract_id": {
"type": "string"
},
"contract_name": {
"type": "string"
},
"country_location": {
"type": "string"
}
}
},
"pricing": {
"type": "object",
"properties": {
"sell_price": {
"type": "object",
"properties": {
"amount_vat_excluded": {
"type": "number"
},
"amount_vat_included": {
"type": "number"
}
}
},
"retail_price": {
"nullable": true
},
"eco_participation": {
"type": "object",
"properties": {
"amount_vat_included": {
"type": "number"
}
}
}
}
},
"packaging": {
"type": "object",
"properties": {
"increment": {
"type": "string"
},
"minimum_quantity": {
"type": "string"
}
}
},
"product_id": {
"type": "string"
},
"is_sellable": {
"type": "boolean"
},
"delivery_offers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"estimate_delivery": {
"type": "object",
"properties": {
"is_express": {
"type": "boolean"
},
"delivery_time": {
"type": "object",
"properties": {
"max": {
"type": "integer"
},
"min": {
"type": "integer"
}
}
},
"free_delivery": {
"type": "object",
"properties": {
"threshold": {
"nullable": true
},
"is_reached": {
"type": "boolean"
}
}
}
}
},
"delivery_countries": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# ManoMano Product (/manomano-product)
**GET** `https://api.piloterr.com/v2/manomano/product`
Extract full product details from a ManoMano product page, including price, seller, brand, images, variants, and stock.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full ManoMano product page URL. Optionally include the `model_id` query parameter to target a specific variant. **Example:** https://www.manomano.fr/p/pergola-retractable-298l-x-213l-x-222h-m-structure-metal-epoxy-anticorrosion-toile-polyester-haute-densite-180-g-m-incluse-gris-17786586?model_id=17784588 **Notes:** - Supported domains: `manomano.fr`, `manomano.co.uk`, `manomano.de`, `manomano.es`, `manomano.it` - If `model_id` is omitted, the default variant is returned. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"brand": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"media": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"large_url": {
"type": "string"
},
"regular_url": {
"type": "string"
},
"thumbnail_url": {
"type": "string"
}
}
}
},
"mm_id": {
"type": "integer"
},
"price": {
"type": "object",
"properties": {
"primary_price": {
"type": "object",
"properties": {
"amount_with_vat": {
"type": "number"
},
"measurement_unit": {
"type": "string"
},
"amount_without_vat": {
"type": "number"
}
}
},
"discount_percentage": {
"type": "integer"
},
"eco_participation_with_vat": {
"type": "number"
}
}
},
"stock": {
"type": "object",
"properties": {
"minimum_value": {
"type": "integer"
},
"increase_quantity": {
"type": "integer"
}
}
},
"title": {
"type": "string"
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"currency": {
"type": "string"
},
"delivery": {
"type": "object",
"properties": {
"weight": {
"type": "integer"
},
"fulfillment": {
"type": "boolean"
}
}
},
"model_id": {
"type": "integer"
},
"offer_id": {
"type": "string"
},
"quantity": {
"type": "object",
"properties": {
"stock": {
"nullable": true
},
"value": {
"type": "integer"
}
}
},
"variants": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"model_id": {
"type": "integer"
},
"is_available": {
"type": "boolean"
}
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# ManoMano Search (/manomano-search)
**GET** `https://api.piloterr.com/v2/manomano/search`
Search for products on ManoMano by category or keyword URL, returning listings with prices, ratings, and seller information.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full ManoMano search or category page URL. **Example:** https://www.manomano.fr/perceuse-1146 **Notes:** - Use any valid ManoMano search or category URL from any supported domain (`manomano.fr`, `manomano.co.uk`, `manomano.de`, `manomano.es`, `manomano.it`) - Pagination and filter parameters present in the URL are respected |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"cpc": {
"type": "number"
},
"url": {
"type": "string"
},
"bulk": {
"type": "boolean"
},
"title": {
"type": "string"
},
"is_b2b": {
"type": "boolean"
},
"is_mmf": {
"type": "boolean"
},
"prices": {
"type": "object",
"properties": {
"main": {
"type": "number"
},
"unit": {
"nullable": true
},
"retail": {
"type": "number"
},
"discount": {
"type": "string"
},
"secondary": {
"nullable": true
}
}
},
"rating": {
"type": "number"
},
"brand_id": {
"type": "integer"
},
"is_local": {
"type": "boolean"
},
"model_id": {
"type": "integer"
},
"offer_id": {
"type": "string"
},
"position": {
"type": "integer"
},
"seller_id": {
"type": "integer"
},
"article_id": {
"type": "integer"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mascus Ad (/mascus-ad)
**GET** `https://api.piloterr.com/v2/mascus/ad`
Get full details for a Mascus used equipment listing: specs, price, seller and description.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Mascus listing URL. Usually taken from `results[].url` in search. **Example:** https://www.mascus.fr/travaux-publics/broyeur-dechets/doppstadt-ak-435-profi/hocmgmfi.html Works on all Mascus country sites (`mascus.fr`, `mascus.de`, `mascus.com`, …). |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"meta_data": {
"type": "object",
"properties": {
"meta_title": {
"type": "string"
}
}
},
"catalog_name": {
"type": "string"
},
"company_city": {
"type": "string"
},
"company_name": {
"type": "string"
},
"user_currency": {
"type": "string"
},
"company_country": {
"type": "string"
},
"asset_details_info": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"asset_pricing_info": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"price_in_user_currency": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mascus Search (/mascus-search)
**GET** `https://api.piloterr.com/v2/mascus/search`
Search used equipment listings on Mascus from a category URL, with pagination that keeps country and category filters.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Mascus category or search URL. The country site comes from the host (`mascus.be`, `mascus.nl`, `mascus.de`, `mascus.com`, …). **Recommended (pagination keeps category + country):** ``` https://www.mascus.be/bouw/graafmachines/be,country.html ``` ``` https://www.mascus.nl/bouw/graafmachines/nl,country.html ``` ``` https://www.mascus.de/baumaschinen/bagger/de,country.html ``` **Also supported:** https://www.mascus.be/+/catalogs=construction&categories=excavators&countries=BE/1,relevance,search.html Filters from the URL are kept on page 2+. |
| `page` | query | `number` | No | Page number (default: `1`). About 40 results per page. **Example:** `2` Works with classic country browse URLs (`…/be,country.html`) and `+/catalogs=…/N,relevance,search.html` URLs. You can also put `?page=2` in the query URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"image_url": {
"type": "string"
},
"rental_ad": {
"type": "boolean"
},
"auction_ad": {
"type": "boolean"
},
"price_euro": {
"type": "integer"
},
"product_id": {
"type": "string"
},
"catalog_name": {
"type": "string"
},
"company_name": {
"type": "string"
},
"category_name": {
"type": "string"
},
"location_city": {
"type": "string"
},
"user_currency": {
"type": "string"
},
"year_of_manufacture": {
"type": "integer"
},
"location_country_code": {
"type": "string"
},
"price_in_user_currency": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mobile Ad (/mobile-ad)
**GET** `https://api.piloterr.com/v2/mobile/ad`
Mobile Ad API. Fetch full car listing data from a mobile.de ad URL or numeric id. Returns pricing, specs, images and features via webunlocker.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Mobile ad URL or numeric ad id. **Examples:** ``` 455111824 ``` ``` https://www.mobile.de/fr/voiture/d%C3%A9tails.html?id=455111824 ``` Region/locale is auto-detected from full URLs; pass `locale` with numeric ids. |
| `locale` | query | `string` | No | Target locale when `query` is a numeric ad id only (default: `fr`). Ignored for full ad URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"make": {
"type": "string"
},
"ad_id": {
"type": "string"
},
"model": {
"type": "string"
},
"power": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"locale": {
"type": "string"
},
"mileage": {
"type": "string"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"features": {
"type": "array",
"items": {
"type": "string"
}
},
"fuel_type": {
"type": "string"
},
"image_url": {
"type": "string"
},
"transmission": {
"type": "string"
},
"first_registration": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mobile Search (/mobile-search)
**GET** `https://api.piloterr.com/v2/mobile/search`
Mobile Search API. Search car listings by URL or make/model code across mobile.de locales. Returns prices and pagination via webunlocker and Next.js RSC parsing.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search URL or make/model code (`ms=`). **Examples:** ``` 20100 ``` ``` https://www.mobile.de/fr/voiture/recherche.html?ms=20100&s=Car&vc=Car ``` ``` https://suchen.mobile.de/fahrzeuge/search.html?s=Car&vc=Car ``` Use **Mobile Ad** on any result `url` or `ad_id` for full listing data. |
| `locale` | query | `string` | No | Target locale when `query` is a make/model code only (default: `fr`). **Examples:** `fr`, `de`, `es`, `en`, `nl`, `pl` Ignored when `query` is a full mobile.de URL. |
| `page` | query | `number` | No | 1-based page override for search URLs (`pageNumber=`, default: from URL or `1`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"make": {
"type": "string"
},
"ad_id": {
"type": "string"
},
"model": {
"type": "string"
},
"power": {
"type": "string"
},
"price": {
"type": "integer"
},
"title": {
"type": "string"
},
"locale": {
"type": "string"
},
"mileage": {
"type": "string"
},
"currency": {
"type": "string"
},
"location": {
"type": "string"
},
"fuel_type": {
"type": "string"
},
"image_url": {
"type": "string"
},
"price_rating": {
"type": "string"
},
"transmission": {
"type": "string"
},
"first_registration": {
"type": "string"
},
"price_rating_label": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"nullable": true
},
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Mr.Bricolage Suggest (/mrbricolage-suggest)
**GET** `https://api.piloterr.com/v2/mrbricolage/suggest`
Live Mr.Bricolage product suggestions for a search keyword or EAN, including title, URL, image, price and currency.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or EAN used to query the Mr.Bricolage live suggest endpoint. Accepts product names, partial titles or numeric identifiers such as EAN barcodes. **Examples:** ``` marteau ``` ``` 3283988157233 ``` |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Newegg Product (/newegg-product)
**GET** `https://api.piloterr.com/v2/newegg/product`
Newegg Product API: fetch full product page data from an item number, product ID or URL. Returns pricing, specs, images, ratings, seller info and promo codes via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Newegg item number, product ID, or full product URL. **Examples:** ``` N82E16819113844 ``` ``` 19-113-844 ``` ``` https://www.newegg.com/amd-ryzen-5-9000-series-ryzen-5-9600x-granite-ridge-socket-am5-desktop-cpu-processor/p/N82E16819113844 ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"upc": {
"type": "string"
},
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"savings": {
"type": "number"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"seller_id": {
"nullable": true
},
"product_id": {
"type": "string"
},
"promo_code": {
"type": "string"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"nullable": true
},
"item_number": {
"type": "string"
},
"seller_name": {
"type": "string"
},
"subcategory": {
"type": "string"
},
"availability": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"bullet_points": {
"type": "array",
"items": {
"type": "string"
}
},
"original_price": {
"type": "integer"
},
"promotion_text": {
"type": "string"
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"group": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Newegg Search (/newegg-search)
**GET** `https://api.piloterr.com/v2/newegg/search`
Newegg Search API: search product listings by keyword or URL on Newegg. Returns prices, ratings, stock flags, marketplace/combo signals and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Newegg search URL. **Examples:** ``` ryzen 5 9600x ``` ``` https://www.newegg.com/p/pl?d=ryzen+5+9600x ``` ``` https://www.newegg.com/p/pl?d=laptops&page=2 ``` Use **Newegg Product** on any result `url` or `product_id` for PDP details. |
| `page` | query | `number` | No | 1-based page number (default: 1). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"is_combo": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"item_number": {
"type": "string"
},
"availability": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"is_marketplace": {
"type": "boolean"
},
"is_refurbished": {
"type": "boolean"
},
"original_price": {
"type": "integer"
},
"promotion_text": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"keyword": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Oceanio Search (/oceanio-search)
**GET** `https://api.piloterr.com/v2/oceanio/search`
Search the Ocean.io B2B database by domain to find similar companies, retrieve contact details, employee profiles, and social network links.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Domain name or keyword to search for similar companies in the Ocean.io B2B database. **Required.** Accepts: - A domain name (e.g. `google.com`, `stripe.com`) - A company name or keyword **Example:** google.com **Notes:** - `results` contains semantically similar companies ranked by relevance score (0–1). - `companies` contains the exact company matching the queried domain. - Employee LinkedIn profiles are returned in the `people` field, keyed by domain. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"people": {
"type": "object",
"properties": {
"google.com": {
"type": "array",
"items": {
"type": "object",
"properties": {
"photo": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
}
}
}
},
"yandex.com": {
"type": "array",
"items": {
"type": "object",
"properties": {
"photo": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
}
}
}
},
"marcombo.com": {
"type": "array",
"items": {
"type": "object",
"properties": {
"photo": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
}
}
}
},
"wordcruncher.com": {
"type": "object",
"properties": {
"photo": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
}
}
}
}
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"score": {
"type": "integer"
},
"domain": {
"type": "string"
},
"phones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"number": {
"type": "string"
},
"country": {
"type": "string"
},
"primary": {
"type": "boolean"
}
}
}
},
"countries": {
"type": "array",
"items": {
"type": "string"
}
},
"company_size": {
"type": "string"
},
"people_count": {
"type": "integer"
},
"website_status": {
"type": "integer"
},
"social_networks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"platform": {
"type": "string"
}
}
}
}
}
}
},
"companies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"emails": {
"type": "array",
"items": {
"type": "string"
}
},
"phones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"number": {
"type": "string"
},
"country": {
"type": "string"
}
}
}
},
"countries": {
"type": "array",
"items": {
"type": "string"
}
},
"company_size": {
"type": "string"
},
"people_count": {
"type": "integer"
},
"social_networks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"platform": {
"type": "string"
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Oscaro Product (/oscaro-product)
**GET** `https://api.piloterr.com/v2/oscaro/product`
Oscaro Product API. Fetch full auto parts product data from an Oscaro product URL. Returns pricing, brand, reference, images and availability via browser rendering and JSON-LD parsing.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Oscaro product page URL (`*-{sku}-p` suffix). **Examples:** ``` https://www.oscaro.com/huile-moteur-motul-102208-15213891-1862-p ``` ``` https://www.oscaro.es/filtro-de-aceite-purflux-l343d-13644654-7-p ``` Region is auto-detected from the URL TLD. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"reference": {
"type": "string"
},
"product_id": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Oscaro Search (/oscaro-search)
**GET** `https://api.piloterr.com/v2/oscaro/search`
Oscaro Search API. Search auto parts by keyword, search URL or category listing on oscaro.com, oscaro.es and oscaro.pt. Returns prices and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword, search URL, or category listing URL (`*-{id}-g`). **Examples:** ``` moteur ai ``` ``` https://www.oscaro.com/fr/search?q=moteur+ai ``` ``` https://www.oscaro.com/huile-moteur-1862-g ``` Use **Oscaro Product** on any result `url` for full PDP data. |
| `region` | query | `string` | No | Target shop when `query` is a keyword (default: `com`). **Examples:** `com`, `fr`, `es`, `pt` Ignored when `query` is a full Oscaro URL. |
| `page` | query | `number` | No | 1-based page number override for category listing URLs (default: read from URL or `1`). Uses `?page=2` on `*-g` pages. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"nullable": true
},
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Ovoko Ad (/ovoko-ad)
**GET** `https://api.piloterr.com/v2/ovoko/ad`
Extract full details from an Ovoko auto parts listing including price, part info, seller data, compatibility list, and stock availability.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Ovoko product listing URL for a specific auto part. **Supported domains:** `ovoko.fr`, `ovoko.de`, `ovoko.es`, `ovoko.it`, `ovoko.pl`, `ovoko.pt`, `ovoko.ro`, `ovoko.lt`, `ovoko.lv`, `ovoko.ee` **Example:** https://www.ovoko.fr/fr/annonce/filtre-a-huile-mann-filter-w71930-128549 **Note:** The URL must point to a specific part listing page. Category and search pages are not supported by this endpoint: use the search endpoint instead. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"part": {
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"brand": {
"type": "string"
},
"category": {
"type": "string"
},
"condition": {
"type": "string"
},
"reference": {
"type": "string"
},
"weight_kg": {
"type": "number"
},
"compatibility": {
"type": "array",
"items": {
"type": "string"
}
},
"dimensions_mm": {
"type": "object",
"properties": {
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"length": {
"type": "integer"
}
}
}
}
},
"price": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"currency": {
"type": "string"
},
"formatted": {
"type": "string"
}
}
},
"stock": {
"type": "object",
"properties": {
"quantity": {
"type": "integer"
},
"available": {
"type": "boolean"
},
"delivery_days": {
"type": "integer"
}
}
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"seller": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"rating": {
"type": "number"
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"reviews_count": {
"type": "integer"
}
}
},
"description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Ovoko Search (/ovoko-search)
**GET** `https://api.piloterr.com/v2/ovoko/search`
Search for auto parts on Ovoko by URL, returning paginated listings with part details, pricing, seller rating, and stock availability.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Ovoko search or category URL for auto parts. **Supported domains:** `ovoko.fr`, `ovoko.de`, `ovoko.es`, `ovoko.it`, `ovoko.pl`, `ovoko.pt`, `ovoko.ro`, `ovoko.lt`, `ovoko.lv`, `ovoko.ee` **Example (keyword search):** https://www.ovoko.fr/fr/recherche?q=filtre+huile **Example (category browse):** https://www.ovoko.fr/fr/categorie/filtration/filtres-a-huile **Note:** Pagination can be added by appending `?page=2` to the URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"part": {
"type": "object",
"properties": {
"brand": {
"type": "string"
},
"condition": {
"type": "string"
},
"reference": {
"type": "string"
}
}
},
"image": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"currency": {
"type": "string"
},
"formatted": {
"type": "string"
}
}
},
"stock": {
"type": "object",
"properties": {
"available": {
"type": "boolean"
},
"delivery_days": {
"type": "integer"
}
}
},
"title": {
"type": "string"
},
"seller": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"rating": {
"type": "number"
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Owler Company Info (/owler-company-info)
**GET** `https://api.piloterr.com/v2/owler/company/info`
Retrieve detailed company intelligence from Owler, including revenue, employee count, CEO info, competitors, funding, and industry sectors.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The company to look up on Owler. Multiple formats are accepted: - **Company slug:** `airbus` - **Owler company URL:** `https://owler.com/company/airbus` **Examples:** https://owler.com/company/tesla The response includes the company's financials, CEO details, headcount, competitors, and industry sectors. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"cg": {
"type": "array",
"items": {
"type": "object",
"properties": {
"founded": {
"type": "string"
},
"revenue": {
"type": "integer"
},
"ownership": {
"type": "string"
},
"ceo_detail": {
"type": "object",
"properties": {
"ceo_pic": {
"type": "string"
},
"last_name": {
"type": "string"
},
"ceo_rating": {
"type": "integer"
},
"first_name": {
"type": "string"
},
"designation": {
"type": "string"
}
}
},
"headquarters": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"country": {
"type": "string"
},
"state_display_name": {
"type": "string"
}
}
},
"revenue_range": {
"type": "string"
},
"total_funding": {
"type": "integer"
},
"employee_count": {
"type": "integer"
},
"employee_range": {
"type": "string"
},
"industry_sectors": {
"type": "array",
"items": {
"type": "string"
}
},
"formatted_funding": {
"type": "string"
},
"formatted_revenue": {
"type": "string"
},
"company_basic_info": {
"type": "object",
"properties": {
"logo": {
"type": "string"
},
"cp_link": {
"type": "string"
},
"website": {
"type": "string"
},
"team_name": {
"type": "string"
},
"company_id": {
"type": "integer"
},
"short_name": {
"type": "string"
}
}
},
"formatted_employee_count": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Owler Search (/owler-search)
**GET** `https://api.piloterr.com/v2/owler/search`
Search for companies on Owler by name or keyword, returning a list of matching companies with slugs, logos, domains, and profile URLs.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The company name or keyword to search for on Owler. **Examples:** tesla The API returns a list of matching companies from Owler's database, including their name, slug, logo, domain, and Owler profile URL. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"domain": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# PagesJaunes Page Info (/pagesjaunes-page-info)
**GET** `https://api.piloterr.com/v2/pagesjaunes/page/info`
Retrieve detailed business information from a PagesJaunes page, including contact details, opening hours, services, payment methods, and SIREN number.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A PagesJaunes business page URL or business ID. **Examples:** https://www.pagesjaunes.fr/pros/56824689 Both the full PagesJaunes URL and the numeric business ID are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"address": {
"type": "string"
},
"activity": {
"type": "string"
},
"services": {
"type": "array",
"items": {
"type": "string"
}
},
"websites": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
},
"business_info": {
"type": "object",
"properties": {
"siren": {
"type": "string"
},
"forme_juridique": {
"type": "string"
},
"effectif_entreprise": {
"type": "string"
},
"creation_d_entreprise": {
"type": "string"
}
}
},
"opening_hours": {
"type": "array",
"items": {
"type": "object",
"properties": {
"day": {
"type": "string"
},
"hours": {
"type": "string"
}
}
}
},
"payment_methods": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# PagesJaunes Search (/pagesjaunes-search)
**GET** `https://api.piloterr.com/v2/pagesjaunes/search`
Search PagesJaunes for French local businesses by category and location, returning paginated results with contact details and ratings.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid PagesJaunes search URL. **Example:** https://www.pagesjaunes.fr/annuaire/chercherlespros?quoiqui=Dentiste&ou=Toulouse+(31000)&univers=pagesjaunes&idOu= You can customize the search by changing the `quoiqui` (what/who) and `ou` (where) parameters in the URL. **Note:** URL-encode the `&` as `%26` when passing the full URL as a GET parameter value. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"address": {
"type": "string"
},
"activity": {
"type": "string"
},
"image_url": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Patreon Profile Info (/patreon-profile-info)
**GET** `https://api.piloterr.com/v2/patreon/profile/info`
Retrieve a Patreon creator profile with patron counts, membership tiers, campaign summary, and creator metadata.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Patreon profile URL or creator vanity name. **Example 1 (URL):** https://www.patreon.com/cfemen **Example 2 (vanity name):** cfemen **Notes:** - Returns campaign metadata, membership tiers, patron counts, and creator details. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"cover": {
"type": "string"
},
"avatar": {
"type": "string"
},
"creator": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"about": {
"nullable": true
},
"avatar": {
"type": "string"
},
"full_name": {
"type": "string"
},
"social_connections": {
"nullable": true
}
}
},
"is_nsfw": {
"type": "boolean"
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"nullable": true
},
"title": {
"nullable": true
},
"published": {
"nullable": true
},
"description": {
"type": "string"
},
"amount_cents": {
"type": "integer"
},
"patron_count": {
"nullable": true
}
}
}
},
"summary": {
"type": "string"
},
"currency": {
"type": "string"
},
"one_liner": {
"nullable": true
},
"created_at": {
"type": "string"
},
"is_monthly": {
"type": "boolean"
},
"patron_count": {
"type": "integer"
},
"published_at": {
"type": "string"
},
"creation_name": {
"type": "string"
},
"creation_count": {
"type": "integer"
},
"paid_member_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Patreon Search (/patreon-search)
**GET** `https://api.piloterr.com/v2/patreon/search`
Search Patreon creators by keyword and retrieve campaign cards with patron counts, post counts, and profile URLs.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword to find Patreon creators. **Example:** creator **Notes:** - Returns matching creator pages with patron counts, post counts, and profile URLs. - Use `pagination.next` or increment the page parameter to fetch additional results. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"query": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"thumb": {
"type": "string"
},
"avatar": {
"type": "string"
},
"is_nsfw": {
"type": "boolean"
},
"summary": {
"nullable": true
},
"post_count": {
"type": "integer"
},
"creator_name": {
"type": "string"
},
"patron_count": {
"nullable": true
},
"creation_name": {
"type": "string"
},
"primary_theme_color": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"prev": {
"type": "string"
},
"hits_total": {
"type": "integer"
},
"pages_total": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"has_prev_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Picard Product (/picard-product)
**GET** `https://api.piloterr.com/v2/picard/product`
Picard Product API. Fetch full frozen food product data from a Picard URL or reference. Returns pricing, images, description, Nutri-Score, Planet-Score, breadcrumb and reviews.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Picard product URL or numeric reference. **Examples:** ``` 050314 ``` ``` https://www.picard.fr/produits/puree-patate-douce-000000000000050314.html ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"gtin": {
"type": "string"
},
"brand": {
"type": "string"
},
"label": {
"type": "string"
},
"price": {
"type": "number"
},
"range": {
"type": "string"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"origin": {
"type": "string"
},
"rating": {
"type": "number"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"packaging": {
"type": "string"
},
"reference": {
"type": "string"
},
"breadcrumb": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"position": {
"type": "integer"
}
}
}
},
"nutriscore": {
"type": "string"
},
"product_id": {
"type": "string"
},
"unit_price": {
"type": "number"
},
"description": {
"type": "string"
},
"planetscore": {
"type": "string"
},
"subcategory": {
"type": "string"
},
"availability": {
"type": "string"
},
"subcategory2": {
"type": "string"
},
"reviews_count": {
"type": "integer"
},
"unit_price_text": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Picard Search (/picard-search)
**GET** `https://api.piloterr.com/v2/picard/search`
Picard Search API. Search frozen food listings on picard.fr by keyword or URL. Parses SFCC Search-UpdateGrid HTML; returns prices, Nutri-Score, ratings and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Picard search URL. **Examples:** ``` patate douce ``` ``` https://www.picard.fr/recherche?q=patate&lang=fr_FR ``` Use **Picard Product** on any result `url` or `product_id` for full PDP data. |
| `page` | query | `number` | No | 1-based page number (24 results per page via SFCC `Search-UpdateGrid`). Default: `1`. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"packaging": {
"type": "string"
},
"reference": {
"type": "string"
},
"nutriscore": {
"type": "string"
},
"product_id": {
"type": "string"
},
"subcategory": {
"type": "string"
},
"availability": {
"type": "string"
},
"reviews_count": {
"nullable": true
},
"unit_price_text": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"count": {
"type": "integer"
},
"has_next": {
"type": "boolean"
},
"per_page": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Pinterest User Info (/pinterest-user-info)
**GET** `https://api.piloterr.com/v2/pinterest/user/info`
Access Pinterest user profiles and analytics for targeted marketing and audience insights.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | The Pinterest profile to retrieve. Accepts a full profile URL or a username. **Accepted formats:** - `https://www.pinterest.com/amandagabriella`: full profile URL - `amandagabriella`: username only **Example:** https://www.pinterest.com/amandagabriella **Notes:** - **Private accounts:** Pinterest does not have private accounts, all public profiles are accessible. - **Deactivated accounts:** Returns a not-found error if the account no longer exists. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"avatar": {
"type": "string"
},
"website": {
"nullable": true
},
"pin_count": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"board_count": {
"type": "integer"
},
"description": {
"nullable": true
},
"is_verified": {
"type": "boolean"
},
"profile_cover": {
"nullable": true
},
"follower_count": {
"type": "integer"
},
"domain_verified": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# PitchBook Company Info (/pitchbook-company-info)
**GET** `https://api.piloterr.com/v2/pitchbook/company/info`
Get a PitchBook company profile: overview, funding, competitors and more via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | PitchBook company id or company profile URL. **Examples:** ``` 11062-99 ``` ``` https://pitchbook.com/profiles/company/149504-14 ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"website": {
"type": "string"
},
"employees": {
"type": "integer"
},
"company_id": {
"type": "string"
},
"description": {
"type": "string"
},
"stock_symbol": {
"type": "string"
},
"year_founded": {
"type": "integer"
},
"corporate_office": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"primary_industry": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# PitchBook Search (/pitchbook-search)
**GET** `https://api.piloterr.com/v2/pitchbook/search`
Search PitchBook for companies, news and profiles by keyword or search URL via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword or PitchBook search URL. **Examples:** ``` airbus ``` ``` https://pitchbook.com/search?q=airbus ``` ``` https://pitchbook.com/profiles/search?q=openai ``` |
| `page` | query | `string` | No | Page number for content search (`/search?q=...`). |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"image": {
"type": "string"
},
"title": {
"type": "string"
},
"category": {
"type": "string"
},
"company_id": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Point.P Product (/pointp-product)
**GET** `https://api.piloterr.com/v2/pointp/product`
Point.P Product API: scrape live `/p/` product pages from a full URL. Get title, images, EAN, pricing, specifications, and breadcrumbs as JSON via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Point.P product page URL (`/p/...-A{id}`). **Example:** https://www.pointp.fr/p/platre-isolation-ite/laine-de-verre-a-souffler-thermo-loft-knauf-insulation-sac-16-A3334230 **Notes:** - Pass the full product URL (not just the ID). - Slug must end with `-A{id}` (e.g. `A3334230`). - Domain must be `pointp.fr`. - **Rejected (400):** URLs without `/p/`, slugs without `-A{id}`, non-Point.P domains. - Use **Point.P Search** to discover `listing_url` values first. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of parsed JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"link": {
"type": "string"
},
"name": {
"type": "string"
},
"brand": {
"type": "string"
},
"media": {
"type": "array",
"items": {
"type": "string"
}
},
"pricing": {
"type": "object",
"properties": {
"unit": {
"type": "string"
},
"price": {
"type": "number"
},
"price_currency": {
"type": "string"
}
}
},
"reference": {
"type": "string"
},
"product_id": {
"type": "string"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Point.P Search (/pointp-search)
**GET** `https://api.piloterr.com/v2/pointp/search`
Point.P Search API: search by keyword or URL and get building-material listings with prices, brands, ratings, units, and pagination as JSON via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Point.P search results URL. **Example (keyword):** colle Resolves to `https://www.pointp.fr/search?q=colle`. **Example (search URL):** https://www.pointp.fr/search?q=colle **Example (page 2):** https://www.pointp.fr/search/page-2?q=colle **Accepted URL patterns:** `/search`, `/search/page-{n}` with param `q` **Notes:** - Pass a keyword or copy the full search URL from pointp.fr. - Use `page` to override the page number, or paginate via `pagination.next`. - Use each result `listing_url` with **Point.P Product** for full PDP data. |
| `page` | query | `number` | No | 1-based page number for pagination. Overrides the page inferred from the URL. Defaults to `1` when omitted. **Example:** `2` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"unit": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "integer"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"reference": {
"type": "string"
},
"product_id": {
"type": "string"
},
"listing_url": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# ProductHunt Product Info (/producthunt-product-info)
**GET** `https://api.piloterr.com/v2/producthunt/product/info`
Retrieve product data from ProductHunt, including tagline, categories, screenshots, reviews rating, and social links.
**Status:** Degraded
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A ProductHunt product URL or product slug. **Examples:** https://www.producthunt.com/products/miro Both the full ProductHunt URL and the product slug are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"tagline": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"description": {
"type": "string"
},
"posts_count": {
"type": "integer"
},
"screenshots": {
"type": "array",
"items": {
"type": "string"
}
},
"pricing_type": {
"nullable": true
},
"social_links": {
"type": "object",
"properties": {
"website": {
"type": "string"
}
}
},
"reviews_rating": {
"type": "number"
},
"reviewers_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Realtor Property (/realtor-property)
**GET** `https://api.piloterr.com/v2/realtor/property`
Extract full property details from a Realtor.com listing URL, including price, specs, photos, agents, features, and location data.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Realtor.com property detail URL. **Example:** https://www.realtor.com/realestateandhomes-detail/1492-E-57th-St_Brooklyn_NY_11234_M46117-61190 **Notes:** - Only Realtor.com `/realestateandhomes-detail/` URLs are accepted. - Copy the listing URL from search results or from realtor.com. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"slug": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
}
}
}
},
"agent": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"agent_id": {
"type": "string"
},
"office_id": {
"type": "string"
},
"agent_name": {
"nullable": true
},
"agent_email": {
"type": "string"
},
"agent_phone": {
"nullable": true
},
"office_name": {
"type": "string"
},
"office_phone": {
"nullable": true
}
}
}
},
"flags": {
"type": "object",
"properties": {
"is_auction": {
"nullable": true
},
"is_pending": {
"type": "boolean"
},
"is_contingent": {
"nullable": true
},
"is_non_deeded": {
"nullable": true
},
"is_short_sale": {
"nullable": true
},
"is_coming_soon": {
"nullable": true
},
"is_foreclosure": {
"nullable": true
},
"is_new_listing": {
"type": "boolean"
},
"is_subdivision": {
"nullable": true
},
"is_price_reduced": {
"nullable": true
},
"is_usda_eligible": {
"type": "boolean"
},
"is_new_construction": {
"nullable": true
},
"is_fractionally_owned": {
"nullable": true
},
"is_price_excludes_land": {
"nullable": true
}
}
},
"local": {
"nullable": true
},
"phones": {
"type": "array",
"items": {}
},
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"status": {
"type": "string"
},
"details": {
"type": "object",
"properties": {
"beds": {
"type": "integer"
},
"logo": {
"nullable": true
},
"name": {
"nullable": true
},
"pool": {
"nullable": true
},
"sqft": {
"nullable": true
},
"text": {
"type": "string"
},
"type": {
"type": "string"
},
"rooms": {
"nullable": true
},
"units": {
"nullable": true
},
"garage": {
"nullable": true
},
"styles": {
"nullable": true
},
"zoning": {
"nullable": true
},
"cooling": {
"nullable": true
},
"heating": {
"nullable": true
},
"roofing": {
"nullable": true
},
"stories": {
"nullable": true
},
"beds_max": {
"nullable": true
},
"beds_min": {
"nullable": true
},
"exterior": {
"nullable": true
},
"lot_sqft": {
"type": "integer"
},
"sqft_max": {
"nullable": true
},
"sqft_min": {
"nullable": true
},
"sub_type": {
"nullable": true
},
"baths_max": {
"nullable": true
},
"baths_min": {
"nullable": true
},
"fireplace": {
"nullable": true
},
"baths_3qtr": {
"type": "integer"
},
"baths_full": {
"type": "integer"
},
"baths_half": {
"nullable": true
},
"garage_max": {
"nullable": true
},
"garage_min": {
"nullable": true
},
"year_built": {
"nullable": true
},
"baths_total": {
"nullable": true
},
"garage_type": {
"nullable": true
},
"construction": {
"nullable": true
},
"year_renovated": {
"nullable": true
},
"baths_consolidated": {
"type": "string"
}
}
},
"history": {
"nullable": true
},
"features": {
"type": "object",
"properties": {
"bedrooms": {
"type": "array",
"items": {
"type": "string"
}
},
"bathrooms": {
"type": "array",
"items": {
"type": "string"
}
},
"land _info": {
"type": "array",
"items": {
"type": "string"
}
},
"other _rooms": {
"type": "array",
"items": {
"type": "string"
}
},
"interior _features": {
"type": "array",
"items": {
"type": "string"
}
},
"garage and _parking": {
"type": "array",
"items": {
"type": "string"
}
},
"heating and _cooling": {
"type": "array",
"items": {
"type": "string"
}
},
"other _property _info": {
"type": "array",
"items": {
"type": "string"
}
},
"building and _construction": {
"type": "array",
"items": {
"type": "string"
}
},
"exterior and _lot _features": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "object",
"properties": {
"slug_id": {
"type": "string"
},
"county_needed_for_uniq": {
"type": "boolean"
}
}
},
"county": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"fips_code": {
"type": "string"
},
"state_code": {
"type": "string"
}
}
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"line": {
"type": "string"
},
"unit": {
"nullable": true
},
"state": {
"type": "string"
},
"country": {
"type": "string"
},
"coordinate": {
"type": "object",
"properties": {
"lat": {
"type": "number"
},
"lon": {
"type": "number"
}
}
},
"state_code": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"street_name": {
"type": "string"
},
"street_number": {
"type": "string"
},
"street_suffix": {
"type": "string"
},
"validation_code": {
"type": "string"
},
"street_direction": {
"type": "string"
},
"street_post_direction": {
"nullable": true
}
}
},
"postal_code": {
"type": "object",
"properties": {
"geo_statistics": {
"type": "object",
"properties": {
"housing_market": {
"type": "object",
"properties": {
"hot_market_badge": {
"type": "string"
}
}
}
}
}
}
},
"search_areas": {
"type": "array",
"items": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"state_code": {
"type": "string"
}
}
}
},
"neighborhoods": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"street_view_url": {
"type": "string"
},
"street_view_metadata_url": {
"type": "string"
}
}
},
"list_date": {
"type": "string"
},
"sold_date": {
"type": "string"
},
"list_price": {
"type": "integer"
},
"sold_price": {
"type": "integer"
},
"advertisers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"href": {
"nullable": true
},
"name": {
"type": "string"
},
"team": {
"nullable": true
},
"type": {
"type": "string"
},
"email": {
"type": "string"
},
"photo": {
"nullable": true
},
"broker": {
"type": "object",
"properties": {
"logo": {
"nullable": true
},
"name": {
"nullable": true
},
"accent_color": {
"nullable": true
},
"designations": {
"type": "array",
"items": {}
},
"fulfillment_id": {
"type": "string"
}
}
},
"office": {
"type": "object",
"properties": {
"href": {
"nullable": true
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"hours": {
"nullable": true
},
"photo": {
"nullable": true
},
"phones": {
"nullable": true
},
"slogan": {
"nullable": true
},
"address": {
"type": "object",
"properties": {
"city": {
"nullable": true
},
"line": {
"nullable": true
},
"state": {
"nullable": true
},
"country": {
"nullable": true
},
"coordinate": {
"nullable": true
},
"state_code": {
"nullable": true
},
"postal_code": {
"nullable": true
}
}
},
"mls_set": {
"type": "string"
},
"lead_email": {
"nullable": true
},
"fulfillment_id": {
"type": "string"
},
"application_url": {
"nullable": true
},
"out_of_community": {
"nullable": true
}
}
},
"phones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ext": {
"nullable": true
},
"type": {
"type": "string"
},
"number": {
"type": "string"
},
"primary": {
"type": "boolean"
},
"trackable": {
"nullable": true
}
}
}
},
"slogan": {
"nullable": true
},
"address": {
"type": "object",
"properties": {
"city": {
"nullable": true
},
"line": {
"nullable": true
},
"state": {
"nullable": true
},
"country": {
"nullable": true
},
"state_code": {
"nullable": true
},
"postal_code": {
"nullable": true
}
}
},
"builder": {
"nullable": true
},
"mls_set": {
"type": "string"
},
"nrds_id": {
"nullable": true
},
"state_license": {
"nullable": true
},
"fulfillment_id": {
"type": "string"
}
}
}
},
"tax_history": {
"nullable": true
},
"list_price_last_change": {
"nullable": true
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Realtor Search (/realtor-search)
**GET** `https://api.piloterr.com/v2/realtor/search`
Search Realtor.com listings from a search URL and retrieve prices, beds, baths, sqft, photos, and property metadata.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Realtor.com search results URL. **Example 1 (state search):** https://www.realtor.com/realestateandhomes-search/New-York_NY **Example 2 (city search):** https://www.realtor.com/realestateandhomes-search/Brooklyn_NY **Notes:** - Only Realtor.com search URLs are accepted. - Filters and pagination parameters in the URL are respected. - Use each result's `permalink` or property detail URL with the Realtor Property endpoint. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"href": {
"type": "string"
}
}
}
},
"location": {
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"line": {
"type": "string"
},
"state": {
"type": "string"
},
"postal_code": {
"type": "string"
}
}
}
}
},
"permalink": {
"type": "string"
},
"list_price": {
"type": "integer"
},
"description": {
"type": "object",
"properties": {
"beds": {
"type": "integer"
},
"sqft": {
"type": "integer"
},
"type": {
"type": "string"
},
"baths": {
"type": "integer"
},
"lot_size": {
"type": "integer"
}
}
},
"property_id": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Reverb Product (/reverb-product)
**GET** `https://api.piloterr.com/v2/reverb/product`
Reverb Product API: fetch full listing data from a Reverb URL or listing id. Returns pricing, specs, images, description and shop info via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Reverb listing URL or numeric listing id. **Examples:** ``` 96933467 ``` ``` https://reverb.com/fr/item/96933467-aria-pro-ii-xx-deluxe-1983-matsumoku-noir ``` The `bk` tracking parameter is stripped automatically. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"make": {
"type": "string"
},
"shop": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"shop_id": {
"type": "string"
},
"location": {
"type": "string"
},
"feedback_percentage": {
"type": "integer"
}
}
},
"year": {
"type": "integer"
},
"model": {
"type": "string"
},
"price": {
"type": "integer"
},
"state": {
"type": "string"
},
"title": {
"type": "string"
},
"finish": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"condition": {
"type": "string"
},
"image_url": {
"type": "string"
},
"inventory": {
"type": "integer"
},
"listing_id": {
"type": "string"
},
"description": {
"type": "string"
},
"listing_type": {
"type": "string"
},
"price_display": {
"type": "string"
},
"offers_enabled": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Reverb Search (/reverb-search)
**GET** `https://api.piloterr.com/v2/reverb/search`
Reverb Search API: search music gear listings by keyword, query string or marketplace URL on Reverb. Returns prices, thumbnails and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword, query string, or full Reverb marketplace URL. **Examples:** ``` guitar ``` ``` query=guitar&product_type=electric-guitars ``` ``` https://reverb.com/marketplace?query=guitar&product_type=electric-guitars ``` Use **Reverb Product** on any result `url` or `listing_id` for full listing data. |
| `page` | query | `number` | No | 1-based page number override (default: read from URL or `1`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"image_url": {
"type": "string"
},
"listing_id": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"nullable": true
},
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Rexel Product (/rexel-product)
**GET** `https://api.piloterr.com/v2/rexel/product`
Get a Rexel France product page: references, EAN, descriptions, images and specs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Rexel France product URL containing `/p/{id}`. **Example:** ``` https://www.rexel.fr/frx/HAGWXF122/p/71742449 ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"ean": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"brand": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rexel_ref": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"breadcrumbs": {
"nullable": true
},
"description": {
"type": "string"
},
"specifications": {
"type": "object",
"properties": {
"gamme": {
"type": "string"
},
"intensite": {
"type": "string"
},
"type_de_pose": {
"type": "string"
},
"tension_nominale": {
"type": "string"
}
}
},
"manufacturer_ref": {
"type": "string"
},
"short_description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Rexel Search (/rexel-search)
**GET** `https://api.piloterr.com/v2/rexel/search`
Search Rexel France by keyword, EAN or search URL. Returns product cards and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword, EAN, or Rexel France search URL. **Examples:** ``` prise ``` ``` 3250617191227 ``` ``` https://www.rexel.fr/frx/search?text=prise ``` |
| `page` | query | `string` | No | Page number (1-based). Page 1 omits `page` in the URL. |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ean": {
"nullable": true
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"brand": {
"type": "string"
},
"image_url": {
"type": "string"
},
"rexel_ref": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"description": {
"type": "string"
},
"manufacturer_ref": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SeLoger Agency Rent (/seloger-agency-rent)
**GET** `https://api.piloterr.com/v2/seloger/agency/rent`
Retrieve paginated rental property listings published by a specific SeLoger agency using its numeric agency ID.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid SeLoger agency ID. **Example:** 18317 **Note:** The agency ID is the numeric identifier found at the end of SeLoger agency profile URLs (e.g. `https://www.seloger.com/.../agence-18317/`). You can find it on any agency page. |
| `page` | query | `number` | No | Page number for paginating through the agency's rental listings. Use this parameter to iterate through multiple pages of results when the agency has more listings than returned in a single call. **Example:** 2 |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"result": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"type": {
"type": "integer"
},
"image": {
"type": "object",
"properties": {
"alt": {
"nullable": true
},
"src": {
"type": "string"
}
}
},
"price": {
"type": "integer"
},
"surface": {
"type": "integer"
},
"rooms_count": {
"type": "integer"
},
"bedrooms_count": {
"type": "integer"
},
"locality_label": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SeLoger Agency Sale (/seloger-agency-sale)
**GET** `https://api.piloterr.com/v2/seloger/agency/sale`
Retrieve paginated sale property listings published by a specific SeLoger agency using its numeric agency ID.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid SeLoger agency ID. **Example:** 18317 **Note:** The agency ID is the numeric identifier found at the end of SeLoger agency profile URLs (e.g. `https://www.seloger.com/.../agence-18317/`). You can retrieve it from any SeLoger agency page. |
| `page` | query | `number` | No | Page number for paginating through the agency's sale listings. Use this parameter to iterate through multiple pages when the agency has more listings than returned in a single call. **Example:** 2 |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"result": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"type": {
"type": "integer"
},
"image": {
"type": "object",
"properties": {
"alt": {
"nullable": true
},
"src": {
"type": "string"
}
}
},
"price": {
"type": "integer"
},
"surface": {
"type": "integer"
},
"rooms_count": {
"type": "integer"
},
"bedrooms_count": {
"type": "integer"
},
"locality_label": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SeLoger Agency (/seloger-agency)
**GET** `https://api.piloterr.com/v2/seloger/agency`
Retrieve detailed profile information for a SeLoger real estate agency including contact details, ratings, reviews, and address.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid SeLoger agency profile URL. **Supported URL formats:** ``` /professionnels/agences-immobilieres/{city}/{slug}/ ``` ``` /professionnels/agents-commerciaux/{city}/{slug}/ ``` **Example:** https://www.seloger.com/professionnels/agences-immobilieres/castanet-tolosan-31320/agence-21719 **Note:** The URL must point to a SeLoger agency profile page. The agency slug and numeric ID are both embedded in the URL. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"logo": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"src": {
"type": "string"
}
}
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"siret": {
"type": "string"
},
"banner": {
"type": "object",
"properties": {
"alt": {
"type": "string"
},
"src": {
"nullable": true
}
}
},
"id_rcu": {
"type": "string"
},
"rating": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"reviews_count": {
"type": "integer"
}
}
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"street": {
"type": "string"
},
"zip_code": {
"type": "string"
}
}
},
"fee_url": {
"type": "string"
},
"reviews": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"result": {
"type": "array",
"items": {}
}
}
},
"website": {
"type": "string"
},
"id_tiers": {
"type": "string"
},
"is_seller": {
"nullable": true
},
"languages": {
"type": "string"
},
"legal_form": {
"nullable": true
},
"sold_count": {
"nullable": true
},
"agency_type": {
"type": "integer"
},
"description": {
"nullable": true
},
"phone_number": {
"type": "string"
},
"intermediary_id": {
"type": "integer"
},
"social_networks": {
"type": "array",
"items": {}
},
"sold_properties": {
"nullable": true
},
"intermediary_type": {
"type": "integer"
},
"environment_source": {
"type": "string"
},
"head_office_address": {
"nullable": true
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SeLoger Property (/seloger-property)
**GET** `https://api.piloterr.com/v2/seloger/property`
Efficiently extract detailed data from a SeLoger real estate listing including agency info, pricing, photos, description, and property criteria.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid SeLoger property listing URL. **Example:** https://www.seloger.com/annonces/achat/appartement/bordeaux-33/capucins-saint-michel-nansouty-saint-genes/234462129.htm **Note:** The URL must point to an active SeLoger property listing page. Expired or removed listings will return an error. Both sale and rental listing URLs are supported. |
| `return_page_source` | query | `boolean` | No | Whether to return the raw HTML source of the property page instead of parsed JSON data. - `false`: Returns structured JSON data (default) - `true`: Returns the raw HTML source of the page **Note:** Enabling this option is useful for custom parsing or debugging, but the response will not be structured JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"agency": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"id_rcu": {
"type": "string"
},
"rating": {
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"review_url": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
},
"address": {
"type": "string"
},
"tier_id": {
"type": "integer"
},
"fees_url": {
"type": "string"
},
"description": {
"nullable": true
},
"website_url": {
"type": "string"
},
"legal_notice": {
"type": "object",
"properties": {
"rcs_siren": {
"type": "string"
},
"legal_form": {
"nullable": true
},
"is_individual": {
"type": "boolean"
},
"share_capital": {
"type": "integer"
},
"social_reason": {
"type": "string"
},
"headquarter_address": {
"type": "string"
},
"financial_guarantee_fund": {
"type": "string"
},
"professional_card_number": {
"nullable": true
},
"financial_guarantee_amount": {
"type": "integer"
},
"professional_card_prefecture": {
"nullable": true
}
}
},
"phone_number": {
"type": "string"
},
"contact_hidden": {
"type": "boolean"
},
"profession_type": {
"type": "string"
},
"profil_page_url": {
"type": "string"
},
"organisation_logo": {
"nullable": true
}
}
},
"seeker": {
"nullable": true
},
"contact": {
"type": "object",
"properties": {
"book_a_visit": {
"type": "object",
"properties": {
"slots": {
"nullable": true
},
"enabled": {
"type": "boolean"
},
"event_type": {
"nullable": true
},
"required_form": {
"type": "boolean"
},
"plato_agency_id": {
"nullable": true
},
"allowed_contracts": {
"nullable": true
},
"unpaid_rent_garanty": {
"type": "boolean"
},
"unpaid_rent_garanty_rate": {
"type": "integer"
}
}
}
}
},
"listing": {
"type": "object",
"properties": {
"url": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"nature": {
"type": "integer"
},
"business_unit_type": {
"type": "string"
}
}
},
"early_access": {
"nullable": true
},
"listing_detail": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"media": {
"type": "object",
"properties": {
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"mobile_url": {
"type": "string"
},
"square_url": {
"type": "string"
},
"default_url": {
"type": "string"
},
"original_url": {
"type": "string"
},
"is_horizontal": {
"type": "boolean"
},
"descriptive_fr": {
"nullable": true
},
"square_large_url": {
"type": "string"
},
"low_resolution_url": {
"type": "string"
},
"photo_description_tags": {
"nullable": true
}
}
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SeLoger Search (/seloger-search)
**GET** `https://api.piloterr.com/v2/seloger/search`
Efficiently extract real estate listings from SeLoger search pages for market insights, property management, and investment analysis.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A valid SeLoger search results URL. Any SeLoger search page URL with filters is supported: location, property type, price range, surface, and number of rooms can all be embedded in the URL. **Supported transaction types:** - `/annonces/achat/`: property for sale - `/annonces/locations/`: property for rent **Example:** https://www.seloger.com/annonces/achat/appartement/bordeaux-33/ **Notes:** - Pagination parameters can be appended directly to the URL - All filters visible in the SeLoger search interface are supported |
| `return_page_source` | query | `boolean` | No | Whether to return the raw HTML source of the search results page instead of parsed JSON data. - `false`: Returns structured JSON array of listings (default) - `true`: Returns the raw HTML source of the page **Default:** `false` **Note:** Useful for debugging or custom parsing workflows. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"epc": {
"nullable": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"rooms": {
"type": "integer"
},
"title": {
"type": "string"
},
"is_new": {
"type": "boolean"
},
"nature": {
"type": "integer"
},
"photos": {
"type": "array",
"items": {
"type": "string"
}
},
"contact": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"id_rcu": {
"type": "string"
},
"img_url": {
"type": "string"
},
"agency_id": {
"type": "integer"
},
"agency_link": {
"nullable": true
},
"agency_page": {
"nullable": true
},
"contact_name": {
"type": "string"
},
"phone_number": {
"type": "string"
},
"is_private_seller": {
"type": "boolean"
}
}
},
"pricing": {
"type": "object",
"properties": {
"price": {
"type": "string"
},
"lifetime": {
"type": "boolean"
},
"raw_price": {
"type": "string"
},
"price_note": {
"nullable": true
},
"monthly_price": {
"type": "integer"
},
"square_meter_price": {
"type": "string"
}
}
},
"surface": {
"type": "number"
},
"position": {
"type": "integer"
},
"zip_code": {
"type": "string"
},
"card_type": {
"type": "string"
},
"transport": {
"nullable": true
},
"city_label": {
"type": "string"
},
"desk_count": {
"type": "integer"
},
"photos_qty": {
"type": "integer"
},
"description": {
"type": "string"
},
"estate_type": {
"type": "string"
},
"video_u_r_l": {
"nullable": true
},
"early_access": {
"nullable": true
},
"is_exclusive": {
"type": "boolean"
},
"bedroom_count": {
"type": "integer"
},
"business_unit": {
"type": "integer"
},
"housing_batch": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
}
}
}
},
"lease_type_id": {
"type": "integer"
},
"district_label": {
"type": "string"
},
"estate_type_id": {
"type": "integer"
},
"publication_id": {
"type": "integer"
},
"classified_u_r_l": {
"type": "string"
},
"optional_criteria": {
"type": "array",
"items": {}
},
"partner_link_type": {
"type": "integer"
},
"highlighting_level": {
"type": "integer"
},
"forced_intermediary": {
"type": "boolean"
},
"transaction_type_id": {
"type": "integer"
},
"virtual_visit_u_r_l": {
"type": "string"
},
"missing_optional_criteria": {
"type": "array",
"items": {}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Shophouzz Product (/shophouzz-product)
**GET** `https://api.piloterr.com/v2/shophouzz/product`
Retrieve full product details from Shophouzz, including title, price, variants, images, vendor, and structured metadata.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full URL of the Shophouzz product page to retrieve details for. **Example:** https://shophouzz.com/products/burke-counter-stool-cognac-vegan-leather-white-oak-veneer-metal-dowel-prvw-vr-186041078 **Notes:** - **Full product URL required:** The query must be the complete product page URL, including the product handle. - **Handle-only format not supported:** Only full URLs starting with `https://shophouzz.com/products/` are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"tags": {
"type": "string"
},
"image": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"alt": {
"nullable": true
},
"src": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"position": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"variant_ids": {
"type": "array",
"items": {}
}
}
},
"title": {
"type": "string"
},
"handle": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"alt": {
"nullable": true
},
"src": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"position": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"variant_ids": {
"type": "array",
"items": {}
}
}
}
},
"vendor": {
"type": "string"
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "string"
}
},
"position": {
"type": "integer"
},
"product_id": {
"type": "integer"
}
}
}
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"sku": {
"type": "string"
},
"grams": {
"type": "integer"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"weight": {
"type": "integer"
},
"barcode": {
"type": "string"
},
"option1": {
"type": "string"
},
"option2": {
"type": "string"
},
"option3": {
"type": "string"
},
"taxable": {
"type": "boolean"
},
"image_id": {
"nullable": true
},
"position": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"weight_unit": {
"type": "string"
},
"quantity_rule": {
"type": "object",
"properties": {
"max": {
"nullable": true
},
"min": {
"type": "integer"
},
"increment": {
"type": "integer"
}
}
},
"price_currency": {
"type": "string"
},
"compare_at_price": {
"type": "string"
},
"requires_shipping": {
"type": "boolean"
},
"fulfillment_service": {
"type": "string"
},
"inventory_management": {
"type": "string"
},
"quantity_price_breaks": {
"type": "array",
"items": {}
},
"compare_at_price_currency": {
"type": "string"
}
}
}
},
"body_html": {
"type": "string"
},
"created_at": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"product_type": {
"type": "string"
},
"published_at": {
"type": "string"
},
"published_scope": {
"type": "string"
},
"template_suffix": {
"nullable": true
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Shopify Apps (/shopify-apps)
**GET** `https://api.piloterr.com/v2/shopify/apps`
Retrieve structured details about any Shopify App Store listing, including title, logo, screenshots, pricing plans, categories, and description.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | URL of the Shopify App Store listing to scrape. **Example:** https://apps.shopify.com/judgeme **Notes:** - The URL must point to a valid listing on `apps.shopify.com`. - The API returns the app title, handle, logo, screenshots, description, pricing plans, and categories. - The app handle is the slug at the end of the URL (e.g. `judgeme` in `https://apps.shopify.com/judgeme`). |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"title": {
"type": "string"
},
"handle": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
},
"pricing": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"price": {
"type": "string"
},
"features": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"categories": {
"type": "array",
"items": {}
},
"description": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Shopify Product (/shopify-product)
**GET** `https://api.piloterr.com/v2/shopify/product`
Extract full product details from any Shopify store product page, including title, variants, pricing, images, inventory, and options.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | URL of a Shopify product page to scrape. **Example:** https://www.decathlon.com/products/simond-backpacking-sleeping-bag-mt500-41-f-synthetic-346446 **Notes:** - The URL must point to a product page on any Shopify-powered store (not limited to a single domain). - The API works with any public Shopify storefront that exposes the standard `products.json` endpoint. - The response includes full product details: title, vendor, variants, images, options, pricing, and inventory. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"tags": {
"type": "string"
},
"image": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"alt": {
"type": "string"
},
"src": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"position": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"handle": {
"type": "string"
},
"vendor": {
"type": "string"
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "string"
}
},
"position": {
"type": "integer"
},
"product_id": {
"type": "integer"
}
}
}
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"sku": {
"type": "string"
},
"grams": {
"type": "integer"
},
"price": {
"type": "string"
},
"title": {
"type": "string"
},
"weight": {
"type": "number"
},
"barcode": {
"type": "string"
},
"option1": {
"type": "string"
},
"option2": {
"type": "string"
},
"taxable": {
"type": "boolean"
},
"position": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"product_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"weight_unit": {
"type": "string"
},
"compare_at_price": {
"type": "string"
},
"inventory_policy": {
"type": "string"
},
"requires_shipping": {
"type": "boolean"
},
"inventory_quantity": {
"type": "integer"
},
"fulfillment_service": {
"type": "string"
},
"inventory_management": {
"type": "string"
}
}
}
},
"body_html": {
"type": "string"
},
"created_at": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"product_type": {
"type": "string"
},
"published_at": {
"type": "string"
},
"published_scope": {
"type": "string"
},
"template_suffix": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SimilarWeb Domain (/similarweb-domain)
**GET** `https://api.piloterr.com/v2/similarweb/domain`
Retrieve website traffic and engagement metrics for any domain from SimilarWeb, including visits, bounce rate, top keywords, and traffic sources.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A domain name to retrieve traffic and engagement metrics for. **Examples:** google.com Provide the bare domain without `https://` or `www.` prefixes. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"category": {
"type": "string"
},
"site_name": {
"type": "string"
},
"engagments": {
"type": "object",
"properties": {
"year": {
"type": "string"
},
"month": {
"type": "string"
},
"visits": {
"type": "string"
},
"bounce_rate": {
"type": "string"
},
"time_on_site": {
"type": "string"
},
"page_per_visit": {
"type": "string"
}
}
},
"global_rank": {
"type": "object",
"properties": {
"rank": {
"type": "integer"
}
}
},
"country_rank": {
"type": "object",
"properties": {
"rank": {
"type": "integer"
},
"country": {
"type": "integer"
},
"country_code": {
"type": "string"
}
}
},
"top_keywords": {
"type": "array",
"items": {
"type": "object",
"properties": {
"cpc": {
"type": "number"
},
"name": {
"type": "string"
},
"volume": {
"type": "integer"
},
"estimated_value": {
"type": "integer"
}
}
}
},
"category_rank": {
"type": "object",
"properties": {
"rank": {
"type": "string"
},
"category": {
"type": "string"
}
}
},
"snapshot_date": {
"type": "string"
},
"traffic_sources": {
"type": "object",
"properties": {
"mail": {
"type": "number"
},
"direct": {
"type": "number"
},
"search": {
"type": "number"
},
"social": {
"type": "number"
},
"referrals": {
"type": "number"
},
"paid_referrals": {
"type": "number"
}
}
},
"large_screenshot": {
"type": "string"
},
"top_country_shares": {
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"country": {
"type": "integer"
},
"country_code": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Similarweb Search (/similarweb-search)
**GET** `https://api.piloterr.com/v2/similarweb/search`
Search the Similarweb database to retrieve websites, companies, mobile apps, search keywords, and technologies associated with a given domain or keyword.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Domain name or keyword to search for in the Similarweb database. **Example:** google.com **Notes:** - **Domain search:** Use a full domain (e.g. `google.com`) to find related websites, apps, companies, and technologies associated with that domain. - **Keyword search:** Use a plain keyword (e.g. `amazon`) to retrieve broader associations across the Similarweb index. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"apps": {
"type": "object",
"properties": {
"appStore": {
"type": "array",
"items": {}
},
"googlePlay": {
"type": "array",
"items": {}
}
}
},
"keywords": {
"type": "array",
"items": {
"type": "string"
}
},
"websites": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"image": {
"type": "string"
},
"isVirtual": {
"type": "boolean"
}
}
}
},
"companies": {
"type": "array",
"items": {}
},
"technologies": {
"type": "array",
"items": {}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SnapEDA Product (/snapeda-product)
**GET** `https://api.piloterr.com/v2/snapeda/product`
SnapEDA Product API: fetch full part page data from a SnapEDA URL or MPN/manufacturer slug. Returns CAD models, datasheet, RoHS, distributor pricing and availability via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | SnapEDA part page URL or `MPN/manufacturer` slug. **Examples:** ``` CYPD3175-24LQXQ/Cypress Semiconductor ``` ``` https://www.snapeda.com/parts/CYPD3175-24LQXQ/Cypress%20Semiconductor/view-part/ ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw rendered HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"rohs": {
"type": "boolean"
},
"formats": {
"type": "array",
"items": {
"type": "string"
}
},
"part_id": {
"type": "string"
},
"pricing": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "string"
},
"currency": {
"type": "string"
},
"quantity": {
"type": "integer"
},
"supplier": {
"type": "string"
},
"price_value": {
"type": "integer"
}
}
}
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"cad_models": {
"type": "object",
"properties": {
"symbol": {
"type": "boolean"
},
"model_3d": {
"type": "boolean"
},
"footprint": {
"type": "boolean"
}
}
},
"description": {
"type": "string"
},
"part_number": {
"type": "string"
},
"availability": {
"type": "string"
},
"manufacturer": {
"type": "string"
},
"datasheet_url": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# SnapEDA Search (/snapeda-search)
**GET** `https://api.piloterr.com/v2/snapeda/search`
SnapEDA Search API: search electronic parts by keyword or URL on SnapMagic Search (ex-SnapEDA). Returns MPN, manufacturer, pricing, CAD availability and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full SnapEDA / SnapMagic search URL. **Examples:** ``` USB Type C ``` ``` https://www.snapeda.com/search/?q=Bluetooth+Module ``` Use **SnapEDA Product** on any result `url` or `part_number`/`manufacturer` for full part data. |
| `page` | query | `number` | No | 1-based page number override (default: read from URL or `1`). SnapEDA returns 20 results per page. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw rendered HTML as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"package": {
"type": "string"
},
"part_id": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"has_symbol": {
"type": "boolean"
},
"description": {
"type": "string"
},
"part_number": {
"type": "string"
},
"availability": {
"type": "string"
},
"manufacturer": {
"type": "string"
},
"has_footprint": {
"type": "boolean"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# StockX Product (/stockx-product)
**GET** `https://api.piloterr.com/v2/stockx/product`
Retrieve detailed product information from StockX, including market prices, bid/ask data, size variants, and product metadata.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A StockX product slug. **Examples:** nike-air-max-1-travis-scott-wheat **Note:** Use product slugs, not internal UUIDs (e.g. do NOT use `c318bbcc-312a-4396-9252-698c203d1dea`). Find slugs in the product URL on stockx.com. |
| `country` | query | `string` | No | Country code for localized pricing and availability. - `US`: United States (default) - `FR`: France - `GB`: United Kingdom - `SV`: El Salvador **Example:** `US` |
| `currency` | query | `string` | No | Currency code for price display. - `USD`: US Dollar (default) - `EUR`: Euro - `GBP`: British Pound **Example:** `USD` |
| `language` | query | `string` | No | Language code for localized content. - `en-US`: English (default) - `fr-FR`: French - `en-GB`: British English **Example:** `en-US` |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"sku": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"brand": {
"type": "string"
},
"image": {
"type": "string"
},
"model": {
"type": "string"
},
"gender": {
"type": "string"
},
"market": {
"type": "object",
"properties": {
"bids": {
"type": "object",
"properties": {
"lowest_ask": {
"type": "integer"
},
"highest_bid": {
"type": "integer"
},
"number_asks": {
"type": "integer"
},
"number_bids": {
"type": "integer"
}
}
},
"sales": {
"type": "object",
"properties": {
"last_sale": {
"type": "integer"
},
"change_value": {
"type": "integer"
},
"sales_last_72h": {
"type": "integer"
},
"change_percentage": {
"type": "number"
}
}
},
"shipping": {
"type": "object",
"properties": {
"standard": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"nullable": true
},
"latest_delivery_date": {
"nullable": true
},
"expected_delivery_date": {
"nullable": true
}
}
},
"express_next_day": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
},
"express_standard": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
},
"express_expedited": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
}
}
}
}
},
"traits": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"category": {
"type": "string"
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"gtins": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"identifier": {
"type": "string"
}
}
}
},
"sizes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"size": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
},
"market": {
"type": "object",
"properties": {
"bids": {
"type": "object",
"properties": {
"lowest_ask": {
"type": "integer"
},
"highest_bid": {
"type": "integer"
},
"number_asks": {
"type": "integer"
},
"number_bids": {
"type": "integer"
}
}
},
"sales": {
"type": "object",
"properties": {
"last_sale": {
"type": "integer"
},
"change_value": {
"type": "integer"
},
"sales_last_72h": {
"type": "integer"
},
"change_percentage": {
"type": "number"
}
}
},
"shipping": {
"type": "object",
"properties": {
"standard": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"nullable": true
},
"latest_delivery_date": {
"nullable": true
},
"expected_delivery_date": {
"nullable": true
}
}
},
"express_next_day": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
},
"express_standard": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
},
"express_expedited": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"lowest_ask": {
"type": "integer"
},
"inventory_type": {
"type": "string"
},
"latest_delivery_date": {
"type": "string"
},
"expected_delivery_date": {
"type": "string"
}
}
}
}
}
}
},
"traits": {
"type": "object",
"properties": {
"size": {
"type": "string"
}
}
}
}
}
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"level": {
"type": "integer"
}
}
}
},
"description": {
"type": "string"
},
"small_image": {
"type": "string"
},
"thumb_image": {
"type": "string"
},
"primary_category": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# StockX Search (/stockx-search)
**GET** `https://api.piloterr.com/v2/stockx/search`
Search StockX for products by name, brand, SKU, or slug and receive a list of matching items with product IDs, slugs, images, and colorways.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search query for StockX products. Can be a product name, brand, SKU, or slug. **Examples:** Air Jordan Retro All three formats are supported: keyword phrases, style codes (SKU), and product slugs. |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"sku": {
"nullable": true
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"brand": {
"type": "string"
},
"image": {
"type": "string"
},
"category": {
"type": "string"
},
"colorway": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# StockX Trends (/stockx-trends)
**GET** `https://api.piloterr.com/v2/stockx/trends`
Retrieve trending products on StockX by category, with options to sort by featured, most active, or release date.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | StockX product category to retrieve trending items for. Accepted values: - `sneakers`: Trending sneakers - `streetwear`: Trending streetwear - `electronics`: Trending electronics - `trading cards`: Trending trading cards - `collectibles`: Trending collectibles - `apparel`: Trending apparel - `handbags`: Trending handbags - `watches`: Trending watches |
| `sort_by` | query | `string` | No | Sort order for trending results. - `featured`: Featured/curated items (default) - `most_active`: Most actively traded items - `release_date`: Newest releases first |
| `country` | query | `string` | No | Country code for localized trending data. - `US`: United States - `FR`: France - `GB`: United Kingdom |
## Responses
### 200 Successful response
```json
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"sku": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"brand": {
"type": "string"
},
"image": {
"type": "string"
},
"category": {
"type": "string"
},
"release_date": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Target Product (/target-product)
**GET** `https://api.piloterr.com/v2/target/product`
Get a Target.com product page: price, description, images and variations via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Target product URL or bare TCIN. **Examples:** ``` 17457624 ``` ``` https://www.target.com/p/-/A-17457624 ``` ``` https://www.target.com/p/l-oreal-paris-magic-root-cover-up-medium-brown-2-0oz/-/A-17457624 ``` |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"upc": {
"type": "string"
},
"url": {
"type": "string"
},
"dpci": {
"type": "string"
},
"name": {
"type": "string"
},
"tcin": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"currency": {
"type": "string"
},
"ornaments": {
"type": "array",
"items": {
"type": "string"
}
},
"highlights": {
"type": "array",
"items": {
"type": "string"
}
},
"variations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"tcin": {
"type": "string"
},
"value": {
"type": "string"
},
"selected": {
"type": "boolean"
}
}
}
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"description": {
"type": "string"
},
"ingredients": {
"nullable": true
},
"review_count": {
"type": "integer"
},
"bullet_descriptions": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Target Search (/target-search)
**GET** `https://api.piloterr.com/v2/target/search`
Search Target.com by keyword or search URL. Returns product cards with price, rating and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Keyword or Target search URL. **Examples:** ``` shampoo ``` ``` root touch up hair dye ``` ``` https://www.target.com/s?searchTerm=shampoo ``` |
| `page` | query | `string` | No | Page number (1-based). |
| `return_page_source` | query | `boolean` | No | When `true`, return rendered HTML instead of JSON. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"tcin": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"rating": {
"type": "integer"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"sponsored": {
"type": "boolean"
},
"review_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# TheFork Restaurant (/thefork-restaurant)
**GET** `https://api.piloterr.com/v2/thefork/restaurant`
Extract a full TheFork restaurant profile from a restaurant page URL, including ratings, tags, photo gallery, opening hours, and AI review summaries via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full TheFork restaurant page URL. **Example:** https://www.thefork.fr/restaurant/salamboo-amilkar-r10892#date=2026-06-13&rankedBy=SEARCH_ENGINE **Notes:** - Only `/restaurant/{slug}-r{id}` URLs on TheFork domains are accepted. - Hash parameters (`#date=...`) are optional and preserved when relevant. - Restaurant pages are JavaScript SPAs; this endpoint uses website rendering to extract ratings, tags, photos, hours, and AI review summaries. - Copy the restaurant URL from search results or from thefork.fr. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"category": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"is_published": {
"type": "boolean"
}
}
}
},
"uuid": {
"type": "string"
},
"image": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"address": {
"type": "object",
"properties": {
"street": {
"type": "string"
},
"country": {
"type": "string"
},
"locality": {
"type": "string"
},
"zip_code": {
"type": "string"
}
}
},
"cuisine": {
"type": "string"
},
"ratings": {
"type": "object",
"properties": {
"thefork": {
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"reviews_count": {
"type": "integer"
}
}
},
"tripadvisor": {
"type": "object",
"properties": {
"rating": {
"type": "number"
},
"reviews_count": {
"type": "integer"
}
}
}
}
},
"currency": {
"type": "string"
},
"avg_price": {
"type": "integer"
},
"description": {
"nullable": true
},
"geolocation": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
}
},
"is_bookable": {
"type": "boolean"
},
"reviews_summary": {
"type": "string"
},
"opening_time_information": {
"type": "object",
"properties": {
"opening_hours": {
"type": "object",
"properties": {
"fri": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
},
"mon": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
},
"sat": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
},
"sun": {
"type": "array",
"items": {}
},
"thu": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
},
"tue": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
},
"wed": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# TheFork Search (/thefork-search)
**GET** `https://api.piloterr.com/v2/thefork/search`
Search TheFork restaurants from a full search or city URL and retrieve ratings, cuisine, prices, availability, review summaries, and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full TheFork search or city listing URL. **Example 1 (search with date, time, and party size):** https://www.thefork.fr/search?cityId=529135&date=2026-06-13&hour=660&partySize=8&timezone=Europe%2FParis **Example 2 (city restaurant listing):** https://www.thefork.fr/restaurants/strasbourg-c529135 **Notes:** - Accepts `thefork.fr`, `thefork.com`, and other TheFork country domains. - Copy the full URL from TheFork, including filters (`cityId`, `date`, `hour`, `partySize`, cuisine tags, etc.). - Pagination uses the `#list25` anchor in `pagination.next_url` (page 2 = `#list25`, page 3 = `#list50`, etc.). - TheFork search pages are JavaScript SPAs; this endpoint uses website rendering to scrape listing cards. - Pass each result's `url` to the TheFork Restaurant endpoint for full profile data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"uuid": {
"type": "string"
},
"image": {
"type": "string"
},
"rating": {
"type": "number"
},
"address": {
"type": "string"
},
"cuisine": {
"type": "string"
},
"currency": {
"type": "string"
},
"avg_price": {
"type": "integer"
},
"timeslots": {
"type": "array",
"items": {}
},
"geolocation": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"__typename": {
"type": "string"
}
}
},
"is_bookable": {
"type": "boolean"
},
"reviews_count": {
"type": "integer"
},
"review_snippets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"__typename": {
"type": "string"
},
"highlights": {
"type": "array",
"items": {
"type": "object",
"properties": {
"end": {
"type": "integer"
},
"start": {
"type": "integer"
},
"__typename": {
"type": "string"
}
}
}
},
"review_snippet_uuid": {
"type": "string"
}
}
}
},
"reviews_summary": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"next_url": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Thomasnet Company (/thomasnet-company)
**GET** `https://api.piloterr.com/v2/thomasnet/company`
Get a full Thomasnet supplier profile from a company URL, including contact details, product categories, and business information.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Thomasnet company profile URL from your browser. **Example:** https://www.thomasnet.com/company/zepole-supply-co-20088400/profile **Example with search context:** https://www.thomasnet.com/company/zepole-supply-co-20088400/profile?searchterm=Table+Forks&heading=31172406 **Notes:** - Only company profile URLs on thomasnet.com are accepted (pages containing `/company/` and `/profile`). - Extra parameters in the URL are optional. You can copy the link exactly as shown on Thomasnet. - You can paste a profile URL from search results or open the company page on thomasnet.com and copy the address bar. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"company": {
"type": "object",
"properties": {
"ads": {
"nullable": true
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"news": {
"nullable": true
},
"tier": {
"type": "string"
},
"type": {
"type": "string"
},
"brands": {
"nullable": true
},
"social": {
"nullable": true
},
"videos": {
"nullable": true
},
"address": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"zip4": {
"nullable": true
},
"state": {
"type": "string"
},
"county": {
"nullable": true
},
"country": {
"type": "string"
},
"address1": {
"type": "string"
},
"address2": {
"nullable": true
},
"address3": {
"nullable": true
},
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"state_name": {
"type": "string"
}
}
},
"heading": {
"type": "object",
"properties": {
"url": {
"nullable": true
},
"name": {
"type": "string"
},
"family_id": {
"type": "string"
},
"heading_id": {
"type": "string"
},
"description": {
"type": "string"
},
"family_name": {
"type": "string"
},
"natural_name": {
"type": "string"
}
}
},
"website": {
"type": "string"
},
"families": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"headings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"nullable": true
},
"name": {
"type": "string"
},
"heading_id": {
"type": "string"
},
"description": {
"type": "string"
},
"family_name": {
"type": "string"
}
}
}
},
"logo_url": {
"nullable": true
},
"premiums": {
"nullable": true
},
"products": {
"nullable": true
},
"personnel": {
"nullable": true
},
"tgrams_id": {
"type": "string"
},
"company_ad": {
"nullable": true
},
"is_claimed": {
"type": "boolean"
},
"logo_title": {
"type": "string"
},
"description": {
"type": "string"
},
"whitepapers": {
"nullable": true
},
"annual_sales": {
"type": "string"
},
"catalog_type": {
"nullable": true
},
"year_founded": {
"type": "string"
},
"is_advertiser": {
"type": "boolean"
},
"primary_phone": {
"type": "string"
},
"heading_brands": {
"nullable": true
},
"location_types": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
}
},
"other_headings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"nullable": true
},
"name": {
"type": "string"
},
"heading_id": {
"type": "string"
},
"description": {
"nullable": true
},
"natural_name": {
"type": "string"
}
}
}
},
"is_top_responder": {
"nullable": true
},
"number_employees": {
"type": "string"
},
"other_activities": {
"nullable": true
},
"xometry_verified": {
"type": "boolean"
},
"is_multi_location": {
"type": "boolean"
},
"main_location_name": {
"nullable": true
},
"is_affiliation_page": {
"type": "boolean"
},
"affiliated_member_of": {
"nullable": true
},
"certification_totals": {
"nullable": true
},
"additional_information": {
"nullable": true
},
"description_by_company": {
"nullable": true
},
"affiliation_contact_url": {
"nullable": true
},
"main_location_tgrams_id": {
"nullable": true
},
"affiliation_company_label": {
"nullable": true
},
"affiliation_featured_members": {
"nullable": true
}
}
},
"products": {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {}
},
"categories": {
"type": "array",
"items": {}
},
"capabilities": {
"type": "array",
"items": {}
},
"configurators": {
"type": "array",
"items": {}
}
}
},
"search_term": {
"type": "string"
},
"business_details": {
"type": "object",
"properties": {
"first_column_sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"text": {
"type": "string"
},
"emit_web_traxs_event": {
"type": "boolean"
}
}
}
},
"label": {
"type": "string"
}
}
}
},
"second_column_sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
}
}
},
"label": {
"type": "string"
}
}
}
}
}
},
"navigation_headings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url_slug": {
"type": "string"
},
"natural_name": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Thomasnet Search (/thomasnet-search)
**GET** `https://api.piloterr.com/v2/thomasnet/search`
Search Thomasnet suppliers from a search or category URL and get company listings, filters, name suggestions, and pagination.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Thomasnet search URL from your browser. **Example 1 (keyword search):** https://www.thomasnet.com/suppliers/search?searchterm=Table+Forks **Example 2 (product category):** https://www.thomasnet.com/suppliers/table-forks-31172406 **Example 3 (page 2):** https://www.thomasnet.com/suppliers/search?searchterm=dz&page_number=2 **Example 4 (older search page):** https://www.thomasnet.com/compsearch.html?searchterm=stainless+steel **Notes:** - Only URLs on thomasnet.com are accepted. - Copy the full URL from your browser after running a search on thomasnet.com. - To go to the next page, change `page_number` in the URL or use the next page link on Thomasnet. - Use each result's `url` with **Thomasnet Company** to get the full supplier profile. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"filters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"count": {
"type": "integer"
},
"label": {
"type": "string"
},
"section": {
"type": "string"
}
}
}
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"rank": {
"type": "string"
},
"tier": {
"type": "string"
},
"type": {
"type": "string"
},
"videos": {
"nullable": true
},
"address": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"zip4": {
"nullable": true
},
"state": {
"type": "string"
},
"county": {
"nullable": true
},
"country": {
"type": "string"
},
"address1": {
"type": "string"
},
"address2": {
"nullable": true
},
"address3": {
"nullable": true
},
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"state_name": {
"type": "string"
}
}
},
"heading": {
"nullable": true
},
"website": {
"type": "string"
},
"distance": {
"nullable": true
},
"headings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"heading_id": {
"type": "string"
},
"natural_name": {
"type": "string"
}
}
}
},
"logo_url": {
"nullable": true
},
"pdm_text": {
"type": "string"
},
"products": {
"nullable": true
},
"tgrams_id": {
"type": "string"
},
"is_claimed": {
"type": "boolean"
},
"logo_title": {
"type": "string"
},
"description": {
"type": "string"
},
"annual_sales": {
"type": "string"
},
"capabilities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"names": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"catalog_type": {
"nullable": true
},
"year_founded": {
"type": "string"
},
"is_advertiser": {
"type": "boolean"
},
"primary_phone": {
"type": "string"
},
"heading_brands": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"is_top_responder": {
"nullable": true
},
"number_employees": {
"type": "string"
},
"other_activities": {
"type": "array",
"items": {
"type": "string"
}
},
"xometry_verified": {
"type": "boolean"
},
"is_multi_location": {
"type": "boolean"
},
"main_location_name": {
"nullable": true
},
"main_location_tgrams_id": {
"nullable": true
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"current": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
},
"search_term": {
"type": "string"
},
"company_name_matches": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"tier": {
"type": "string"
},
"type": {
"type": "string"
},
"social": {
"nullable": true
},
"address": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"zip4": {
"nullable": true
},
"state": {
"type": "string"
},
"county": {
"nullable": true
},
"country": {
"type": "string"
},
"address1": {
"type": "string"
},
"address2": {
"nullable": true
},
"address3": {
"nullable": true
},
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"state_name": {
"type": "string"
}
}
},
"heading": {
"nullable": true
},
"website": {
"nullable": true
},
"headings": {
"nullable": true
},
"logo_url": {
"nullable": true
},
"premiums": {
"nullable": true
},
"personnel": {
"nullable": true
},
"tgrams_id": {
"type": "string"
},
"is_claimed": {
"type": "boolean"
},
"logo_title": {
"type": "string"
},
"description": {
"type": "string"
},
"annual_sales": {
"type": "string"
},
"catalog_type": {
"nullable": true
},
"year_founded": {
"type": "string"
},
"is_advertiser": {
"type": "boolean"
},
"primary_phone": {
"type": "string"
},
"location_types": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
}
},
"is_top_responder": {
"nullable": true
},
"number_employees": {
"type": "string"
},
"other_activities": {
"nullable": true
},
"xometry_verified": {
"type": "boolean"
},
"is_multi_location": {
"type": "boolean"
},
"main_location_name": {
"nullable": true
},
"is_affiliation_page": {
"type": "boolean"
},
"description_by_company": {
"nullable": true
},
"main_location_tgrams_id": {
"nullable": true
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Trustpilot Company Info (/trustpilot-company-info)
**GET** `https://api.piloterr.com/v2/trustpilot/company/info`
Retrieve Trustpilot company profile data, including trust score, star rating, categories, contact info, and review response behavior.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Trustpilot company URL or domain name. **Examples:** https://www.trustpilot.com/review/nordvpn.com Both the full Trustpilot review URL and the company domain are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"stars": {
"type": "integer"
},
"activity": {
"type": "object",
"properties": {
"is_claimed": {
"type": "boolean"
},
"reply_behavior": {
"type": "object",
"properties": {
"reply_percentage": {
"type": "number"
},
"average_days_to_reply": {
"type": "number"
}
}
}
}
},
"is_closed": {
"type": "boolean"
},
"breadcrumb": {
"type": "object",
"properties": {
"mid_level_display_name": {
"type": "string"
},
"top_level_display_name": {
"type": "string"
},
"bottom_level_display_name": {
"type": "string"
}
}
},
"categories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"rank": {
"type": "string"
},
"is_primary": {
"type": "boolean"
}
}
}
},
"is_claimed": {
"type": "boolean"
},
"trust_score": {
"type": "number"
},
"website_url": {
"type": "string"
},
"contact_info": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"display_name": {
"type": "string"
},
"website_title": {
"type": "string"
},
"similar_business": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"domain": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Upwork Freelancer (/upwork-freelancer)
**GET** `https://api.piloterr.com/v2/upwork/freelancer`
Upwork Freelancer API: fetch public freelancer profile data from a profile ID, username or URL. Returns bio, hourly rate, skills, ratings and agency info via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Upwork freelancer profile ID, username, or full profile URL. **Examples:** ``` ~0161ad91037ca6144b ``` ``` https://www.upwork.com/freelancers/~0161ad91037ca6144b ``` ``` https://www.upwork.com/freelancers/adnans2 ``` Returns a flat profile object with bio, hourly rate, skills, ratings, and agency info. Pair with **Upwork Jobs** for role discovery or **Upwork Services Info** seller URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"rating": {
"type": "number"
},
"skills": {
"type": "array",
"items": {
"type": "string"
}
},
"user_id": {
"type": "string"
},
"currency": {
"type": "string"
},
"location": {
"type": "string"
},
"username": {
"nullable": true
},
"verified": {
"type": "boolean"
},
"is_agency": {
"type": "boolean"
},
"profile_id": {
"type": "string"
},
"agency_name": {
"type": "string"
},
"description": {
"type": "string"
},
"hourly_rate": {
"type": "integer"
},
"total_hours": {
"type": "integer"
},
"review_count": {
"type": "integer"
},
"job_success_score": {
"type": "integer"
},
"profile_image_url": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Upwork Jobs (/upwork-jobs)
**GET** `https://api.piloterr.com/v2/upwork/jobs`
Upwork Jobs API: search freelance job listings by skill slug or URL. Returns budgets, skills, experience level and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Upwork skill slug or full freelance-jobs URL. **Examples:** ``` api-development ``` ``` https://www.upwork.com/freelance-jobs/api-development/ ``` Returns `{ skill, title, results[], pagination }`. Use **Upwork Freelancer** to enrich a candidate profile from a freelancer URL. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"skill": {
"type": "string"
},
"title": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"job_id": {
"type": "string"
},
"skills": {
"type": "array",
"items": {
"type": "string"
}
},
"currency": {
"type": "string"
},
"duration": {
"type": "string"
},
"job_type": {
"type": "string"
},
"posted_at": {
"type": "string"
},
"budget_max": {
"nullable": true
},
"budget_min": {
"type": "integer"
},
"posted_ago": {
"type": "string"
},
"description": {
"type": "string"
},
"listing_url": {
"type": "string"
},
"hours_per_week": {
"nullable": true
},
"experience_level": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Upwork Services Info (/upwork-services-info)
**GET** `https://api.piloterr.com/v2/upwork/services/info`
Upwork Services Info API: fetch Project Catalog product details from a product URL. Returns packages, pricing, seller, media and breadcrumbs via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Upwork Project Catalog product URL. **Example:** https://www.upwork.com/services/product/development-it-mobile-app-developer-android-ios-flutter-application-hybrid-apps-1403673770703212544 Returns a flat product object with description, pricing tiers, packages, seller info, and media. Discover listings with **Upwork Services**, then pass any `listing_url` here. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"media": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"seller": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"pricing": {
"type": "object",
"properties": {
"currency": {
"type": "string"
},
"price_max": {
"type": "integer"
},
"price_min": {
"type": "integer"
}
}
},
"packages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"price": {
"type": "integer"
},
"currency": {
"type": "string"
},
"delivery_days": {
"type": "integer"
}
}
}
},
"product_id": {
"type": "string"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Upwork Services (/upwork-services)
**GET** `https://api.piloterr.com/v2/upwork/services`
Upwork Services API: browse Project Catalog listings by category slug or URL. Returns prices, product IDs and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Upwork Project Catalog category slug or full services URL. **Examples:** ``` mobile-apps ``` ``` https://www.upwork.com/services/mobile-apps ``` Returns `{ category, name, results[], pagination }`. Use **Upwork Services Info** for full product details from any `listing_url`. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"rating": {
"nullable": true
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"price_from": {
"type": "integer"
},
"product_id": {
"type": "string"
},
"listing_url": {
"type": "string"
}
}
}
},
"category": {
"type": "string"
},
"pagination": {
"type": "object",
"properties": {
"next": {
"nullable": true
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"nullable": true
},
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Vinted Item (/vinted-item)
**GET** `https://api.piloterr.com/v2/vinted/item`
Fetch a Vinted listing by item URL or numeric ID. Parses the public item page (Next.js payload), returns a normalized snake_case object with photos, attributes, shipping, pricing, and optional seller profile.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Vinted item URL or numeric item ID. **Examples (URL: recommended):** https://www.vinted.com/items/9437210920-purple-prom-dress **Example (ID only):** 9437210920 **Notes:** - Domain is inferred from the URL (`vinted.fr`, `vinted.com`, …). - With a bare numeric ID, Piloterr tries the main Vinted domains until the item is found. - Use `region` to force a domain and skip the multi-domain lookup. |
| `region` | query | `string` | No | Force the Vinted domain when `query` is a bare item ID only. **Examples:** ``` com ``` ``` https://www.vinted.com ``` ``` https://www.vinted.de ``` **Notes:** - Ignored when `query` is a full item URL. - Speeds up lookups when you already know the marketplace. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"city": {
"type": "string"
},
"login": {
"type": "string"
},
"photo": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
}
},
"business": {
"type": "boolean"
},
"item_count": {
"type": "integer"
},
"profile_url": {
"type": "string"
},
"country_code": {
"type": "string"
},
"verification": {
"type": "object",
"properties": {
"email": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"available": {
"type": "boolean"
}
}
},
"google": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"available": {
"type": "boolean"
},
"verified_at": {
"type": "string"
}
}
}
}
},
"country_title": {
"type": "string"
},
"feedback_count": {
"type": "integer"
},
"feedback_reputation": {
"type": "integer"
},
"positive_feedback_count": {
"type": "integer"
}
}
},
"color": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"is_main": {
"type": "boolean"
},
"full_size_url": {
"type": "string"
}
}
}
},
"status": {
"type": "string"
},
"can_buy": {
"type": "boolean"
},
"plugins": {
"type": "object",
"properties": {
"favourite": {
"type": "object",
"properties": {
"seller_id": {
"type": "integer"
},
"is_favourite": {
"type": "boolean"
},
"favourite_count": {
"type": "integer"
}
}
},
"breadcrumbs": {
"type": "object",
"properties": {
"brand_id": {
"type": "integer"
},
"catalog_id": {
"type": "integer"
},
"breadcrumbs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
}
}
},
"description": {
"type": "object",
"properties": {
"description": {
"type": "string"
}
}
},
"item_status": {
"type": "object",
"properties": {
"is_draft": {
"type": "boolean"
},
"promoted": {
"type": "boolean"
},
"is_closed": {
"type": "boolean"
},
"is_hidden": {
"type": "boolean"
},
"is_reserved": {
"type": "boolean"
},
"transaction_permitted": {
"type": "boolean"
}
}
}
}
},
"brand_id": {
"type": "integer"
},
"category": {
"type": "string"
},
"promoted": {
"type": "boolean"
},
"seller_id": {
"type": "integer"
},
"attributes": {
"type": "object",
"properties": {
"size": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"value": {
"type": "string"
}
}
},
"color": {
"type": "string"
},
"status": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"value": {
"type": "string"
}
}
},
"upload_date": {
"type": "string"
}
}
},
"catalog_id": {
"type": "integer"
},
"size_title": {
"type": "string"
},
"description": {
"type": "string"
},
"instant_buy": {
"type": "boolean"
},
"uploaded_at": {
"type": "string"
},
"is_favourite": {
"type": "boolean"
},
"favourite_count": {
"type": "integer"
},
"pricing_services": {
"type": "object",
"properties": {
"services": {
"type": "object",
"properties": {
"shipping": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"is_free": {
"type": "boolean"
},
"final_price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"original_price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
}
}
},
"buyer_protection": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"is_free": {
"type": "boolean"
},
"final_price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"original_price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
}
}
}
}
},
"total_amount": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"original_asking_amount": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
}
}
},
"shipping_details": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"discount": {
"nullable": true
},
"is_pickup_only": {
"type": "boolean"
},
"is_free_shipping": {
"type": "boolean"
},
"are_multiple_shipping_options_available": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Vinted Search (/vinted-search)
**GET** `https://api.piloterr.com/v2/vinted/search`
Search Vinted second-hand listings by keyword or catalog URL across vinted.fr, vinted.com and other domains. Returns results and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword **or** full Vinted catalog URL. Filters from the URL query string are forwarded to the internal Vinted API. **Examples (keyword):** nike **Example (catalog URL):** https://www.vinted.com/catalog?search_text=dresses%20for%20woman **Notes:** - Keyword searches default to `vinted.fr` unless you set `region`. - Catalog URLs keep their domain (`vinted.com`, `vinted.de`, etc.). |
| `page` | query | `number` | No | Page number. Default `1`. |
| `per_page` | query | `number` | No | Results per page. Default `24`. |
| `order` | query | `string` | No | Optional sort order. - `newest_first` ``` relevance ``` ``` price_low_to_high ``` ``` price_high_to_low ``` |
| `region` | query | `string` | No | Force the Vinted domain when `query` is a keyword only. **Examples:** ``` fr ``` ``` https://www.vinted.fr ``` ``` https://www.vinted.de ``` |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"path": {
"type": "string"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"login": {
"type": "string"
},
"photo": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
}
},
"business": {
"type": "boolean"
},
"profile_url": {
"type": "string"
}
}
},
"photo": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"is_main": {
"type": "boolean"
}
}
},
"price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"is_main": {
"type": "boolean"
},
"image_no": {
"type": "integer"
}
}
}
},
"status": {
"type": "string"
},
"item_box": {
"type": "object",
"properties": {
"item_id": {
"type": "integer"
},
"first_line": {
"type": "string"
},
"second_line": {
"type": "string"
}
}
},
"promoted": {
"type": "boolean"
},
"is_visible": {
"type": "boolean"
},
"size_title": {
"type": "string"
},
"brand_title": {
"type": "string"
},
"service_fee": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
},
"is_favourite": {
"type": "boolean"
},
"content_source": {
"type": "string"
},
"favourite_count": {
"type": "integer"
},
"total_item_price": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"currency_code": {
"type": "string"
}
}
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Vinted User Info (/vinted-user-info)
**GET** `https://api.piloterr.com/v2/vinted/user/info`
Retrieve public Vinted seller profile by member URL or user ID. Returns the raw `/api/v2/users/{id}` object: login, photo, feedback stats, followers, verification, bundle discounts, and listing counts.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Vinted member profile URL, direct API URL, or numeric user ID. **Examples (profile URL):** https://www.vinted.fr/member/272737992-fiorediottobre7 **Example (API URL):** https://www.vinted.fr/api/v2/users/272737992 **Example (ID only):** 272737992 **Notes:** - Domain is inferred from the URL (`vinted.fr`, `vinted.com`, …). - Accepts `/member/{id}`, `/member/{id}-{login}`, `/users/{id}`, or a bare numeric ID. - Returns the raw Vinted user object from `/api/v2/users/{id}`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"city": {
"type": "string"
},
"path": {
"type": "string"
},
"about": {
"type": "string"
},
"email": {
"nullable": true
},
"login": {
"type": "string"
},
"photo": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"extra": {
"type": "object",
"properties": {}
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"is_hidden": {
"type": "boolean"
},
"temp_uuid": {
"nullable": true
},
"thumbnails": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"original_size": {
"nullable": true
}
}
}
},
"orientation": {
"nullable": true
},
"full_size_url": {
"type": "string"
},
"is_suspicious": {
"type": "boolean"
},
"dominant_color": {
"type": "string"
},
"high_resolution": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"timestamp": {
"type": "integer"
},
"orientation": {
"nullable": true
}
}
},
"dominant_color_opaque": {
"type": "string"
}
}
},
"locale": {
"type": "string"
},
"anon_id": {
"type": "string"
},
"city_id": {
"nullable": true
},
"birthday": {
"nullable": true
},
"business": {
"type": "boolean"
},
"contacts": {
"nullable": true
},
"currency": {
"type": "string"
},
"is_hated": {
"type": "boolean"
},
"hates_you": {
"type": "boolean"
},
"is_online": {
"type": "boolean"
},
"moderator": {
"type": "boolean"
},
"real_name": {
"nullable": true
},
"can_bundle": {
"type": "boolean"
},
"country_id": {
"type": "integer"
},
"fundraiser": {
"type": "object",
"properties": {
"id": {
"nullable": true
},
"active": {
"type": "boolean"
},
"currency": {
"type": "string"
},
"percentage": {
"type": "integer"
},
"feature_disabled": {
"type": "boolean"
}
}
},
"item_count": {
"type": "integer"
},
"updated_on": {
"type": "integer"
},
"profile_url": {
"type": "string"
},
"country_code": {
"type": "string"
},
"is_favourite": {
"type": "boolean"
},
"localization": {
"type": "string"
},
"verification": {
"type": "object",
"properties": {
"email": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"available": {
"type": "boolean"
}
}
},
"google": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"available": {
"type": "boolean"
},
"verified_at": {
"type": "string"
}
}
},
"facebook": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"available": {
"type": "boolean"
},
"verified_at": {
"nullable": true
}
}
}
}
},
"country_title": {
"type": "string"
},
"hide_feedback": {
"type": "boolean"
},
"is_on_holiday": {
"type": "boolean"
},
"last_loged_on": {
"type": "string"
},
"account_status": {
"type": "integer"
},
"feedback_count": {
"type": "integer"
},
"bundle_discount": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"enabled": {
"type": "boolean"
},
"user_id": {
"type": "integer"
},
"fraction": {
"type": "string"
},
"discounts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fraction": {
"type": "string"
},
"minimal_item_count": {
"type": "integer"
}
}
}
},
"minimal_item_count": {
"type": "integer"
}
}
},
"default_address": {
"nullable": true
},
"expose_location": {
"type": "boolean"
},
"followers_count": {
"type": "integer"
},
"following_count": {
"type": "integer"
},
"iso_locale_code": {
"type": "string"
},
"account_ban_date": {
"nullable": true
},
"business_account": {
"nullable": true
},
"can_view_profile": {
"type": "boolean"
},
"country_iso_code": {
"type": "string"
},
"facebook_user_id": {
"nullable": true
},
"given_item_count": {
"type": "integer"
},
"last_loged_on_ts": {
"type": "string"
},
"taken_item_count": {
"type": "integer"
},
"avg_response_time": {
"nullable": true
},
"is_account_banned": {
"type": "boolean"
},
"share_profile_url": {
"type": "string"
},
"total_items_count": {
"type": "integer"
},
"action_restriction": {
"nullable": true
},
"msg_template_count": {
"type": "integer"
},
"business_account_id": {
"nullable": true
},
"contacts_permission": {
"nullable": true
},
"country_title_local": {
"type": "string"
},
"feedback_reputation": {
"type": "integer"
},
"is_catalog_moderator": {
"type": "boolean"
},
"location_description": {
"nullable": true
},
"third_party_tracking": {
"type": "boolean"
},
"allow_direct_messaging": {
"type": "boolean"
},
"following_brands_count": {
"type": "integer"
},
"neutral_feedback_count": {
"type": "integer"
},
"negative_feedback_count": {
"type": "integer"
},
"positive_feedback_count": {
"type": "integer"
},
"is_publish_photos_agreed": {
"type": "boolean"
},
"is_bpf_price_prominence_applied": {
"type": "boolean"
},
"is_catalog_role_marketing_photos": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Walmart Product (/walmart-product)
**GET** `https://api.piloterr.com/v2/walmart/product`
Extract full Walmart product page (PDP) data from a /ip/ URL: pricing, ratings, images, fulfillment, specs, badges, and availability via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Walmart product page URL. **Example:** https://www.walmart.com/ip/Fresh-Rockit-Crisp-Sweet-Miniature-Apples-3lb-Tub/309762096 **Notes:** - The URL must point to a Walmart product detail page containing `/ip/` in the path. - The numeric ID at the end of the URL is the Walmart `us_item_id`. - Copy the full URL from walmart.com (including query params if present). - Walmart PDP data is loaded via JavaScript; this endpoint uses browser rendering. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"pac": {
"nullable": true
},
"upc": {
"type": "string"
},
"esrb": {
"nullable": true
},
"idml": {
"type": "object",
"properties": {
"videos": {
"nullable": true
},
"warranty": {
"nullable": true
},
"drug_guide": {
"nullable": true
},
"esrb_rating": {
"nullable": true
},
"ingredients": {
"type": "object",
"properties": {
"ingredients": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
},
"active_ingredients": {
"nullable": true
},
"inactive_ingredients": {
"nullable": true
},
"active_ingredient_name": {
"nullable": true
}
}
},
"mpaa_rating": {
"nullable": true
},
"size_charts": {
"nullable": true
},
"infographics": {
"nullable": true
},
"social_media": {
"nullable": true
},
"ar_experience": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"gen_ai_details": {
"type": "object",
"properties": {
"gen_ai_concise_summary": {
"nullable": true
},
"gen_ai_description_bullet": {
"nullable": true
}
}
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"choking_hazards": {
"nullable": true
},
"enriched360_spin": {
"nullable": true
},
"long_description": {
"type": "string"
},
"short_description": {
"type": "string"
},
"product_highlights": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"icon_url": {
"nullable": true
}
}
}
},
"has_marketing_description": {
"type": "boolean"
},
"interactive_product_video": {
"nullable": true
},
"interactive_product_assets": {
"nullable": true
},
"product360_image_container": {
"nullable": true
}
}
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"brand": {
"type": "string"
},
"model": {
"nullable": true
},
"badges": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {}
},
"flags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"key": {
"type": "string"
},
"rank": {
"type": "integer"
},
"text": {
"type": "string"
},
"type": {
"type": "string"
},
"query": {
"type": "string"
},
"style_id": {
"type": "string"
},
"__typename": {
"type": "string"
}
}
}
},
"groups": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"members": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"key": {
"type": "string"
},
"rank": {
"type": "integer"
},
"text": {
"type": "string"
},
"type": {
"type": "string"
},
"icon_id": {
"type": "string"
},
"sla_date": {
"nullable": true
},
"sla_text": {
"nullable": true
},
"style_id": {
"type": "string"
},
"action_id": {
"type": "string"
},
"__typename": {
"type": "string"
},
"member_type": {
"type": "string"
},
"text_values": {
"nullable": true
},
"sla_date_iso": {
"nullable": true
},
"badge_content": {
"nullable": true
},
"text_template": {
"nullable": true
}
}
}
},
"__typename": {
"type": "string"
}
}
}
},
"labels": {
"nullable": true
},
"groups_v2": {
"type": "array",
"items": {
"type": "object",
"properties": {
"pos": {
"type": "string"
},
"flow": {
"type": "string"
},
"name": {
"type": "string"
},
"members": {
"type": "array",
"items": {
"type": "object",
"properties": {
"mem_id": {
"type": "string"
},
"content": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"nullable": true
},
"type": {
"type": "string"
},
"value": {
"type": "string"
},
"style_id": {
"type": "string"
},
"action_id": {
"nullable": true
},
"cont_desc": {
"nullable": true
},
"action_content": {
"nullable": true
}
}
}
},
"mem_type": {
"type": "string"
},
"mem_style_id": {
"type": "string"
},
"content_variants": {
"nullable": true
}
}
}
}
}
}
}
}
},
"ebooks": {
"nullable": true
},
"gender": {
"nullable": true
},
"pet_rx": {
"type": "object",
"properties": {
"eligible": {
"type": "boolean"
},
"single_dispense": {
"nullable": true
}
}
},
"vision": {
"type": "object",
"properties": {
"age_group": {
"nullable": true
},
"vision_center_approved": {
"type": "boolean"
}
}
},
"is_byod": {
"type": "boolean"
},
"rewards": {
"nullable": true
},
"rh_path": {
"type": "string"
},
"category": {
"type": "object",
"properties": {
"path": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"category_path_id": {
"type": "string"
}
}
},
"class_id": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"intent": {
"type": "string"
},
"store_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"address_id": {
"nullable": true
},
"postal_code": {
"type": "string"
},
"pickup_location": {
"type": "object",
"properties": {
"store_id": {
"type": "string"
},
"access_type": {
"nullable": true
},
"access_point_id": {
"nullable": true
}
}
},
"mp_pickup_location": {
"nullable": true
},
"state_or_province_code": {
"type": "string"
}
}
},
"offer_id": {
"type": "string"
},
"redirect": {
"type": "object",
"properties": {
"replaced_by_item_id": {
"type": "string"
},
"replaced_by_product_id": {
"type": "string"
},
"replaced_by_product_url_text": {
"type": "string"
}
}
},
"show_atc": {
"type": "boolean"
},
"variants": {
"type": "array",
"items": {}
},
"brand_url": {
"type": "string"
},
"discounts": {
"type": "object",
"properties": {
"discounted_value": {
"nullable": true
},
"discount_meta_data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"price": {
"type": "object",
"properties": {
"price": {
"nullable": true
},
"price_string": {
"nullable": true
},
"price_display": {
"type": "string"
}
}
},
"savings": {
"type": "object",
"properties": {
"amount": {
"nullable": true
},
"percent": {
"nullable": true
},
"price_string": {
"type": "string"
}
}
},
"unit_price": {
"type": "object",
"properties": {
"price": {
"nullable": true
},
"price_string": {
"nullable": true
}
}
},
"comparison_price": {
"type": "object",
"properties": {
"price": {
"nullable": true
},
"price_string": {
"nullable": true
}
}
},
"unit_price_display_condition": {
"nullable": true
}
}
}
}
}
},
"image_map": {
"type": "object",
"properties": {}
},
"item_type": {
"nullable": true
},
"lang_urls": {
"nullable": true
},
"mhmd_flag": {
"type": "boolean"
},
"pre_order": {
"type": "object",
"properties": {
"is_pre_order": {
"type": "boolean"
},
"release_date": {
"nullable": true
}
}
},
"seller_id": {
"type": "string"
},
"tire_size": {
"nullable": true
},
"viscosity": {
"nullable": true
},
"blitz_item": {
"nullable": true
},
"claim_rate": {
"nullable": true
},
"class_type": {
"type": "string"
},
"image_info": {
"type": "object",
"properties": {
"all_images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"zoomable": {
"type": "boolean"
}
}
}
},
"thumbnail_url": {
"type": "string"
},
"thumbnail_aspect_ratio": {
"type": "string"
}
}
},
"offer_type": {
"type": "string"
},
"price_info": {
"type": "object",
"properties": {
"savings": {
"nullable": true
},
"cap_type": {
"nullable": true
},
"tax_info": {
"nullable": true
},
"was_price": {
"nullable": true
},
"base_price": {
"nullable": true
},
"drop_price": {
"type": "boolean"
},
"list_price": {
"nullable": true
},
"ship_price": {
"nullable": true
},
"unit_price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"best_value": {
"nullable": true
},
"price_string": {
"type": "string"
},
"support_text": {
"nullable": true
},
"currency_unit": {
"type": "string"
},
"variant_price_string": {
"nullable": true
}
}
},
"price_range": {
"type": "object",
"properties": {
"max_price": {
"nullable": true
},
"min_price": {
"nullable": true
},
"price_string": {
"nullable": true
},
"currency_unit": {
"nullable": true
},
"denominations": {
"nullable": true
}
}
},
"current_price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"best_value": {
"nullable": true
},
"price_string": {
"type": "string"
},
"support_text": {
"nullable": true
},
"currency_unit": {
"type": "string"
},
"price_display": {
"type": "string"
},
"variant_price_string": {
"type": "string"
}
}
},
"savings_amount": {
"nullable": true
},
"additional_fees": {
"type": "object",
"properties": {
"duty_fee": {
"type": "object",
"properties": {
"price": {
"type": "integer"
},
"price_string": {
"type": "string"
}
}
},
"country_name": {
"nullable": true
},
"estimated_total_price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"price_string": {
"type": "string"
}
}
},
"shipping_and_import_fee": {
"type": "object",
"properties": {
"price": {
"type": "integer"
},
"price_string": {
"type": "string"
}
}
},
"gift_card_margin_and_fee": {
"nullable": true
}
}
},
"comparison_price": {
"nullable": true
},
"is_price_reduced": {
"type": "boolean"
},
"subscription_price": {
"nullable": true
},
"volume_price_tiers": {
"type": "array",
"items": {}
},
"price_display_codes": {
"type": "object",
"properties": {
"rollback": {
"nullable": true
},
"clearance": {
"nullable": true
},
"submap_type": {
"nullable": true
},
"is_b2_b_price": {
"type": "boolean"
},
"reduced_price": {
"nullable": true
},
"price_display_type": {
"type": "string"
},
"price_per_unit_uom": {
"type": "string"
},
"final_cost_by_weight": {
"nullable": true
},
"price_display_condition": {
"nullable": true
},
"eligible_for_associate_discount": {
"type": "boolean"
}
}
},
"price_reduced_display": {
"nullable": true
},
"secondary_offer_boost": {
"nullable": true
},
"walmart_funded_amount": {
"nullable": true
},
"w_plus_early_access_price": {
"nullable": true
}
}
},
"promo_data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"terms": {
"type": "string"
},
"metadata": {
"type": "string"
},
"description": {
"type": "string"
},
"template_data": {
"type": "object",
"properties": {
"flag_type": {
"nullable": true
},
"image_url": {
"type": "string"
},
"apr_string": {
"nullable": true
},
"price_string": {
"nullable": true
},
"finance_data_promotion_type": {
"nullable": true
}
}
},
"installments_promotion": {
"nullable": true
},
"no_interest_installments_promotion": {
"nullable": true
}
}
}
},
"sales_unit": {
"type": "string"
},
"ship_as_is": {
"type": "boolean"
},
"us_item_id": {
"type": "string"
},
"e_gift_card": {
"nullable": true
},
"is_preowned": {
"type": "boolean"
},
"is_qsr_item": {
"type": "boolean"
},
"order_limit": {
"type": "integer"
},
"seller_name": {
"type": "string"
},
"seller_type": {
"type": "string"
},
"wfs_enabled": {
"type": "boolean"
},
"zeekit_data": {
"nullable": true
},
"annual_event": {
"nullable": true
},
"collectibles": {
"nullable": true
},
"condition_v2": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"group_code": {
"type": "integer"
}
}
},
"item_type_v2": {
"type": "string"
},
"lmp_eligible": {
"type": "boolean"
},
"member_limit": {
"nullable": true
},
"subscription": {
"type": "object",
"properties": {
"subscription_eligible": {
"type": "boolean"
},
"subscription_submessage": {
"type": "string"
},
"show_subscription_module": {
"type": "boolean"
},
"subscription_transactable": {
"type": "boolean"
}
}
},
"trust_badges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"subtype": {
"nullable": true
}
}
}
},
"variants_map": {
"type": "object",
"properties": {}
},
"aaia_brand_id": {
"nullable": true
},
"canonical_url": {
"type": "string"
},
"external_info": {
"nullable": true
},
"grading_label": {
"type": "string"
},
"is_collection": {
"type": "boolean"
},
"is_qsr_seller": {
"type": "boolean"
},
"pickup_option": {
"type": "object",
"properties": {
"sla_tier": {
"nullable": true
},
"store_id": {
"nullable": true
},
"store_name": {
"nullable": true
},
"access_types": {
"type": "array",
"items": {
"type": "string"
}
},
"availability_status": {
"type": "string"
}
}
},
"primary_offer": {
"type": "object",
"properties": {
"warranty": {
"nullable": true
}
}
},
"return_policy": {
"type": "object",
"properties": {
"returnable": {
"type": "boolean"
},
"free_returns": {
"type": "boolean"
},
"return_window": {
"type": "object",
"properties": {
"value": {
"type": "integer"
},
"unit_type": {
"type": "string"
}
}
},
"return_location": {
"type": "string"
},
"return_policy_text": {
"type": "string"
},
"return_policy_type": {
"type": "string"
},
"returnable_to_store": {
"type": "boolean"
},
"holiday_return_enabled": {
"type": "boolean"
},
"return_policy_condition": {
"nullable": true
},
"return_policy_text_code": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"data": {
"nullable": true
}
}
},
"return_policy_clarity_text": {
"type": "string"
},
"return_policy_clarity_text_more": {
"nullable": true
}
}
},
"snap_eligible": {
"type": "boolean"
},
"average_rating": {
"type": "number"
},
"condition_type": {
"type": "string"
},
"has_care_plans": {
"type": "boolean"
},
"motor_oil_type": {
"nullable": true
},
"personalizable": {
"type": "boolean"
},
"promo_discount": {
"nullable": true
},
"promo_eligible": {
"type": "boolean"
},
"sem_store_data": {
"nullable": true
},
"shipping_price": {
"nullable": true
},
"show_condition": {
"type": "boolean"
},
"top_promo_text": {
"nullable": true
},
"add_on_services": {
"nullable": true
},
"can_add_to_list": {
"type": "boolean"
},
"experience_type": {
"nullable": true
},
"group_meta_data": {
"nullable": true
},
"is_customizable": {
"type": "boolean"
},
"is_wplus_member": {
"type": "boolean"
},
"order_min_limit": {
"type": "integer"
},
"pet_rx_eligible": {
"nullable": true
},
"product_type_id": {
"type": "string"
},
"seller_logo_url": {
"nullable": true
},
"shipping_option": {
"type": "object",
"properties": {
"sla_tier": {
"nullable": true
},
"ship_price": {
"nullable": true
},
"ship_method": {
"nullable": true
},
"delivery_date": {
"nullable": true
},
"max_delivery_date": {
"nullable": true
},
"availability_status": {
"type": "string"
},
"international_shipping": {
"nullable": true
}
}
},
"tire_load_index": {
"nullable": true
},
"buy_now_eligible": {
"type": "boolean"
},
"condition_offers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"price_string": {
"type": "string"
}
}
},
"offer_id": {
"type": "string"
},
"condition": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"text": {
"type": "string"
},
"group_code": {
"type": "integer"
},
"group_text": {
"type": "string"
}
}
},
"more_conditions": {
"nullable": true
},
"top_boosted_offer": {
"nullable": true
},
"availability_status": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"display": {
"type": "string"
}
}
}
}
}
},
"event_attributes": {
"type": "object",
"properties": {
"price_flip": {
"type": "boolean"
},
"special_buy": {
"type": "boolean"
}
}
},
"fsa_eligible_ind": {
"type": "boolean"
},
"fulfillment_type": {
"type": "string"
},
"has_seller_badge": {
"nullable": true
},
"is_free_warranty": {
"type": "boolean"
},
"is_replenishable": {
"type": "boolean"
},
"ozark_attributes": {
"type": "object",
"properties": {
"exact_address": {
"type": "boolean"
},
"shipping_promise": {
"nullable": true
}
}
},
"primary_shelf_id": {
"type": "string"
},
"product_location": {
"type": "array",
"items": {
"type": "object",
"properties": {
"display_value": {
"type": "string"
}
}
}
},
"secondary_offers": {
"type": "array",
"items": {}
},
"special_cta_type": {
"type": "string"
},
"suppress_reviews": {
"type": "boolean"
},
"variant_criteria": {
"type": "array",
"items": {}
},
"weight_increment": {
"type": "integer"
},
"wireless_carrier": {
"type": "string"
},
"catalog_seller_id": {
"type": "integer"
},
"fulfillment_badge": {
"nullable": true
},
"fulfillment_label": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sla": {
"nullable": true
},
"message": {
"type": "string"
},
"postal_code": {
"nullable": true
},
"delivery_date": {
"nullable": true
},
"location_text": {
"type": "string"
},
"shipping_text": {
"type": "string"
},
"fulfillment_text": {
"type": "string"
},
"fulfillment_type": {
"type": "string"
},
"fulfillment_method": {
"type": "string"
},
"address_eligibility": {
"type": "boolean"
},
"w_plus_fulfillment_text": {
"nullable": true
},
"check_store_availability": {
"nullable": true
}
}
}
},
"fulfillment_title": {
"type": "string"
},
"grading_type_code": {
"nullable": true
},
"has_home_services": {
"nullable": true
},
"ironbank_category": {
"type": "string"
},
"legal_restriction": {
"type": "boolean"
},
"number_of_reviews": {
"type": "integer"
},
"return_attributes": {
"type": "object",
"properties": {
"frequency_of_return": {
"type": "string"
}
}
},
"selected_offer_id": {
"type": "string"
},
"short_description": {
"type": "string"
},
"tire_speed_rating": {
"nullable": true
},
"top_boosted_offer": {
"type": "object",
"properties": {
"offer_id": {
"nullable": true
},
"seller_id": {
"nullable": true
},
"date_string": {
"nullable": true
},
"seller_name": {
"nullable": true
},
"price_string": {
"nullable": true
},
"display_price": {
"nullable": true
},
"shipping_cost": {
"nullable": true
},
"fulfillment_promise": {
"nullable": true
}
}
},
"wfs_provider_name": {
"nullable": true
},
"early_access_event": {
"type": "boolean"
},
"preowned_condition": {
"type": "string"
},
"primary_product_id": {
"nullable": true
},
"primary_us_item_id": {
"nullable": true
},
"shipping_cost_type": {
"nullable": true
},
"abstract_product_id": {
"nullable": true
},
"availability_status": {
"type": "string"
},
"buy_box_suppression": {
"type": "boolean"
},
"fulfillment_options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"intent": {
"type": "boolean"
},
"selected": {
"type": "boolean"
},
"view_only": {
"type": "boolean"
},
"__typename": {
"type": "string"
},
"restricted": {
"nullable": true
},
"order_limit": {
"nullable": true
},
"location_text": {
"type": "string"
},
"speed_details": {
"nullable": true
},
"is_low_in_stock": {
"nullable": true
},
"inventory_status": {
"nullable": true
},
"availability_type": {
"type": "string"
},
"available_quantity": {
"nullable": true
},
"max_order_quantity": {
"nullable": true
},
"availability_status": {
"type": "string"
},
"subscription_submessage": {
"type": "string"
}
}
}
},
"fulfillment_summary": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sla": {
"type": "object",
"properties": {
"rank": {
"type": "integer"
},
"unit_of_measure": {
"type": "string"
},
"measurement_value": {
"type": "integer"
}
}
},
"store_id": {
"type": "string"
},
"is_express": {
"type": "boolean"
},
"fulfillment": {
"type": "string"
},
"regular_sla": {
"nullable": true
},
"delivery_date": {
"nullable": true
},
"store_timezone": {
"nullable": true
},
"fulfillment_badge": {
"nullable": true
},
"fulfillment_price": {
"nullable": true
},
"max_delivery_date": {
"nullable": true
},
"is_free_for_w_plus": {
"nullable": true
},
"calculated_sla_days": {
"type": "integer"
},
"fulfillment_methods": {
"type": "array",
"items": {
"type": "string"
}
},
"store_delivery_date": {
"nullable": true
},
"fulfillment_discount": {
"nullable": true
},
"out_of_country_eligible": {
"nullable": true
},
"store_max_delivery_date": {
"nullable": true
}
}
}
},
"gifting_eligibility": {
"type": "boolean"
},
"is_lmp_alcohol_item": {
"type": "boolean"
},
"product_disclaimers": {
"type": "array",
"items": {}
},
"seller_display_name": {
"type": "string"
},
"seller_review_count": {
"nullable": true
},
"show_buy_with_wplus": {
"type": "boolean"
},
"special_cta_context": {
"type": "string"
},
"special_instruction": {
"type": "boolean"
},
"static_message_type": {
"type": "string"
},
"associated_bundle_id": {
"nullable": true
},
"flower_delivery_info": {
"type": "array",
"items": {}
},
"is_early_access_item": {
"type": "boolean"
},
"selected_variant_ids": {
"type": "array",
"items": {}
},
"shipping_restriction": {
"type": "boolean"
},
"show_add_on_services": {
"type": "boolean"
},
"hide_atc_for_services": {
"type": "boolean"
},
"is_lmp_alcohol_seller": {
"type": "boolean"
},
"secondary_offer_price": {
"type": "object",
"properties": {
"tax_info": {
"nullable": true
},
"current_price": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"price_type": {
"nullable": true
},
"price_string": {
"type": "string"
}
}
}
}
},
"seller_average_rating": {
"nullable": true
},
"show_fulfillment_link": {
"type": "boolean"
},
"subscription_eligible": {
"type": "boolean"
},
"additional_offer_count": {
"nullable": true
},
"availability_status_v2": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"display": {
"type": "string"
}
}
},
"is_get_it_now_eligible": {
"type": "boolean"
},
"seller_store_front_url": {
"nullable": true
},
"tobacco_static_mesages": {
"type": "array",
"items": {}
},
"variant_product_id_map": {
"type": "object",
"properties": {}
},
"is_native1_p_egift_card": {
"nullable": true
},
"manufacturer_product_id": {
"type": "string"
},
"new_condition_product_id": {
"nullable": true
},
"transactable_offer_count": {
"type": "integer"
},
"wireless_third_party_url": {
"nullable": true
},
"display_offer_level_image": {
"type": "boolean"
},
"availability_in_nearby_store": {
"nullable": true
},
"check_store_availability_atc": {
"type": "boolean"
},
"available_fulfillment_options": {
"type": "array",
"items": {
"type": "string"
}
},
"check_availability_global_dfs": {
"type": "boolean"
},
"item_page_availability_status": {
"type": "string"
},
"max_reward_among_all_variants": {
"nullable": true
},
"show_highlights_seller_rating": {
"type": "boolean"
},
"fulfillment_type_classification": {
"type": "string"
},
"is_part_of_low_return_rate_test": {
"type": "boolean"
},
"special_order_quantity_eligible": {
"type": "boolean"
},
"show_explore_other_conditions_cta": {
"type": "boolean"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Walmart Search (/walmart-search)
**GET** `https://api.piloterr.com/v2/walmart/search`
Scrape Walmart.com search result pages from a full URL and retrieve product cards with pricing, ratings, availability, and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Walmart search results URL. **Example (keyword search):** https://www.walmart.com/search?q=apples **Example (page 2):** https://www.walmart.com/search?q=apples&page=2 **Notes:** - Copy the full URL from walmart.com after running a search. - Supports keyword queries (`q=`), pagination (`page=`), and facet filters. - Walmart search pages are JavaScript SPAs; this endpoint uses browser rendering. - Pass each result `url` to the Walmart Product endpoint for full PDP data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"brand": {
"nullable": true
},
"image": {
"type": "string"
},
"price": {
"type": "number"
},
"price_info": {
"type": "object",
"properties": {
"unit_price": {
"type": "string"
},
"line_price_display": {
"type": "string"
}
}
},
"us_item_id": {
"type": "string"
},
"seller_name": {
"type": "string"
},
"canonical_url": {
"type": "string"
},
"average_rating": {
"type": "number"
},
"fulfillment_type": {
"type": "string"
},
"is_sponsored_flag": {
"type": "boolean"
},
"number_of_reviews": {
"type": "integer"
},
"availability_status_v2": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"display": {
"type": "string"
}
}
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "boolean"
},
"page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"max_page": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Walmart Suggest (/walmart-suggest)
**GET** `https://api.piloterr.com/v2/walmart/suggest`
Get Walmart.com search autocomplete suggestions for a keyword via browser rendering, with optional US ZIP code localization.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword for Walmart autocomplete suggestions. **Example:** apples **Notes:** - Partial keywords return related query suggestions (e.g. `apples` → `applesauce`, `apple juice`). - Mirrors the Walmart.com search typeahead behavior. - Walmart typeahead is loaded via JavaScript; this endpoint uses browser rendering. |
| `zipcode` | query | `string` | No | Optional US ZIP code to localize Walmart store and inventory context for suggestions. **Example:** 95829 **Notes:** - 5-digit US ZIP code format. - Omit to use Walmart's default location context. - When provided, affects localized availability context on walmart.com. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"type": {
"type": "string"
},
"image_url": {
"type": "string"
},
"analytics_fm": {
"type": "integer"
},
"display_name": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Antibot (/website-antibot)
**GET** `https://api.piloterr.com/v2/website/antibot`
Detect which anti-bot protection protects a website before you scrape. Get the vendor, confidence level, and matching clues from a single URL check.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full URL or domain name to analyze for anti-bot protection. Must be a valid hostname. **Examples:** ``` https://www.example.com ``` ``` example.com ``` **Notes:** - Passive fingerprinting from HTTP headers, cookies, and page content. - Challenge pages (403, 503) still count as a successful analysis. |
| `timeout` | query | `number` | No | Maximum request timeout in seconds. Defaults to `15`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"status": {
"type": "integer"
},
"vendor": {
"type": "string"
},
"final_url": {
"type": "string"
},
"confidence": {
"type": "string"
},
"detections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"vendor": {
"type": "string"
},
"evidence": {
"type": "array",
"items": {
"type": "string"
}
},
"confidence": {
"type": "string"
}
}
}
},
"requested_url": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Crawler (/website-crawler)
**GET** `https://api.piloterr.com/v2/website/crawler`
Fetch public pages with fast HTTP crawling (no JavaScript). Best first choice for static or server-rendered HTML; use Rendering or WebUnlocker when JS or anti-bot blocks you.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A public website URL to fetch with the Crawler engine (HTTP only, no JavaScript). Must include `http://` or `https://`. **Example:** https://example.com **Choosing the right engine:** - Start here for static or server-rendered HTML ([Website Crawler](https://www.piloterr.com/library/website-crawler)). - Need JavaScript execution? Use [Website Rendering](https://www.piloterr.com/library/website-rendering) instead. - Blocked by Cloudflare/DataDome on a whitelisted domain? Use [Website WebUnlocker](https://www.piloterr.com/library/website-webunlocker). See [Website Scraping guide](https://www.piloterr.com/blog/website-crawler-vs-rendering-vs-webunlocker) for a full comparison. |
| `allow_redirects` | query | `boolean` | No | Controls whether the crawler should follow HTTP redirects. - `true`: Follow redirects automatically (**default**) - `false`: Stop at the first HTTP response without following any redirect **Notes:** - **Set to `false`** when you need to inspect the redirect chain itself (e.g. detecting 301 vs 302 codes). |
| `return_page_source` | query | `boolean` | No | Controls whether the response returns the raw, unprocessed HTML source of the page. - `false`: Return processed/parsed content (**default**) - `true`: Return the exact HTML source as delivered by the web server **Notes:** - Useful for debugging or when you need the precise markup before any transformation. |
## Responses
### 200 Successful response
```json
{
"type": "string"
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Email Phone Extractor (/website-email-phone-extractor)
**GET** `https://api.piloterr.com/v2/website/email_phone_extractor`
Extract emails and phone numbers from websites for comprehensive contact details, including social media profiles across 12+ platforms.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A website URL to crawl for contact information. Must include the `http://` or `https://` protocol. The crawler will follow internal links ("a" anchors) to find contact details across the site. **Example:** https://www.hexa.cc **Notes:** - **GDPR:** Results may contain personal data. Ensure you have a legitimate reason for collection under applicable regulations. |
| `country_code` | query | `string` | No | An ISO 3166-1 alpha-2 country code used to improve the accuracy of phone number validation and parsing. - `FR`: France - `US`: United States - `DE`: Germany - `GB`: United Kingdom - (any valid ISO country code) **Notes:** - Without a country code, phone numbers may not be validated accurately across different regional formats. - **Recommended** when the target site is country-specific. **Example:** country_code=FR |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"emails": {
"type": "array",
"items": {
"type": "string"
}
},
"contact_pages": {
"type": "array",
"items": {}
},
"phone_numbers": {
"type": "array",
"items": {}
},
"reddit_profiles": {
"type": "array",
"items": {}
},
"tiktok_profiles": {
"type": "array",
"items": {}
},
"twitter_profiles": {
"type": "array",
"items": {
"type": "string"
}
},
"youtube_channels": {
"type": "array",
"items": {}
},
"facebook_profiles": {
"type": "array",
"items": {}
},
"linkedin_profiles": {
"type": "array",
"items": {
"type": "string"
}
},
"snapchat_profiles": {
"type": "array",
"items": {}
},
"telegram_channels": {
"type": "array",
"items": {}
},
"instagram_profiles": {
"type": "array",
"items": {
"type": "string"
}
},
"pinterest_profiles": {
"type": "array",
"items": {}
},
"crunchbase_profiles": {
"type": "array",
"items": {}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Rendering (/website-rendering)
**GET** `https://api.piloterr.com/v2/website/rendering`
Render JavaScript-heavy pages in a headless browser and return post-render HTML. Use when Crawler returns empty DOM; escalate to WebUnlocker if bot protection blocks the session.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A public website URL to render in a headless browser. Must include `http://` or `https://`. **Example:** https://example.com **Choosing the right engine:** - Static HTML already in the page? Try [Website Crawler](https://www.piloterr.com/library/website-crawler) first (1 credit, faster). - This endpoint ([Website Rendering](https://www.piloterr.com/library/website-rendering)) runs JavaScript and returns post-render HTML. - Still blocked after rendering? Escalate to [Website WebUnlocker](https://www.piloterr.com/library/website-webunlocker) if the domain is whitelisted. See [Website Scraping guide](https://www.piloterr.com/blog/website-crawler-vs-rendering-vs-webunlocker) for a full comparison. |
| `wait_in_seconds` | query | `number` | No | Number of seconds to wait after page load before returning the rendered HTML. Useful for pages with delayed rendering or animations. **Notes:** - Increasing this value adds latency to every request. Use only when a page's content loads after a delay. |
| `wait_for` | query | `string` | No | A CSS or XPath selector to wait for before returning the rendered HTML. The headless browser will hold the response until the specified element appears in the DOM. Supported selector types: - **ID selector:** `#loading-container` - **Class selector:** `.content-loaded` - **Combined selector:** `div.main-content#user-profile` **Example:** wait_for=#loading-container **Notes:** - If the element never appears, the request will time out according to the `timeout` parameter. |
| `block_ads` | query | `boolean` | No | Controls whether the headless browser should block ad requests during rendering. - `false`: Ads are loaded normally (**default**) - `true`: Ad network requests are blocked, resulting in cleaner HTML and faster rendering **Notes:** - Blocking ads can speed up rendering on ad-heavy pages and reduce noise in the extracted HTML. |
| `timeout` | query | `number` | No | Maximum number of seconds to wait for the page to fully load before returning whatever has been rendered so far. - Accepts integer values representing seconds **Notes:** - Setting a lower timeout may result in incomplete HTML for slow-loading pages. - Combine with `wait_for` to ensure critical elements are present before the timeout fires. |
| `browser_instructions` | query | `array` | No | JSON **array** sent in the **POST body** (not a query string, not a stringified JSON string). status.computer browser workers only execute instructions when this field is a real array (`browser_instructions` / `browserInstructions`). A JSON array of browser navigation instructions to execute sequentially during page rendering. Allows controlling scrolling and other browser actions to trigger dynamic content loading or simulate human behavior. Currently supports two instruction types: - `scroll`: Scrolls the page by a precise number of pixels (horizontally and/or vertically) - `scroll_to_bottom`: Automatically scrolls to the bottom of the page --- **Instruction type: `scroll`** Parameters: - `type`: `"scroll"` - `x`: Pixels to scroll horizontally (positive = right, negative = left) - `y`: Pixels to scroll vertically (positive = down, negative = up) - `duration` *(optional)*: Duration in seconds for smooth scrolling. Default: `0` (instant) - `wait_time_s` *(optional)*: Seconds to wait after the instruction completes. Default: `0` **Example payload:** ```json { "type": "scroll", "x": 0, "y": 4115, "duration": 5, "wait_time_s": 2 } ``` --- **Instruction type: `scroll_to_bottom`** Parameters: - `type`: `"scroll_to_bottom"` - `duration` *(optional)*: Duration in seconds for smooth scrolling. Default: `0` (instant) - `wait_time_s` *(optional)*: Seconds to wait after the instruction completes. Default: `0` **Example payload:** ```json { "type": "scroll_to_bottom", "duration": 3, "wait_time_s": 2 } ``` --- **Full request example (multiple instructions):** ```json { "query": "https://www.comparably.com/companies/airbus", "wait_for": "#ng-state", "browser_instructions": [ {"type": "scroll", "x": 0, "y": 1000, "duration": 2, "wait_time_s": 1}, {"type": "scroll", "x": 0, "y": 2000, "duration": 3, "wait_time_s": 1}, {"type": "scroll_to_bottom", "duration": 4, "wait_time_s": 2} ] } ``` **Notes:** - **Execution order:** Instructions are executed sequentially in the specified order - **Error handling:** If one instruction fails, subsequent ones continue to execute - **Smooth scrolling (`duration > 0`)** takes more time but better simulates human behavior and avoids bot detection - **Increase `timeout`** when using instructions with significant cumulative durations |
## Responses
### 200 Successful response
```json
{
"type": "string"
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Screenshot (/website-screenshot)
**GET** `https://api.piloterr.com/v2/website/screenshot`
Browser-like Screenshot API that captures webpages as PNG/JPEG/WebP or PDF for previews, QA, reporting, monitoring, and archiving no headless setup.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `url` | query | `string` | Yes | The URL of the webpage to capture. Examples: ``` https://www.amazon.com/s?k=pokemon ``` ``` https://www.cdiscount.com/sante-mieux-vivre/materiel-medical/soin-domicile/l-1650608.html ``` ``` https://github.com/seaweedfs/seaweedfs ``` |
| `format` | query | `string` (`jpeg`, `jpg`, `pdf`, `png`, `webp`) | No | The output format of the screenshot. Available formats: - `webp` - `jpeg` - `jpg` - `png` - `pdf` |
| `device` | query | `string` (`galaxy_fold4`, `galaxy_s23`, `galaxy_s23_ultra`, `galaxy_s8`, `huawei_mate_50_pro`, `huawei_p60_pro`, `ipad`, `iphone_12`, `iphone_13_mini`, `iphone_13_pro_max`, `iphone_14_pro`, `iphone_14_pro_max`, `iphone_x`, `pixel_5`, `pixel_6a`, `pixel_7_pro`, `redmi_note_11`, `redmi_note_12_pro`) | No | The device type to emulate for the screenshot. Available devices: - `iphone_14_pro_max` - `iphone_14_pro` - `iphone_13_pro_max` - `iphone_13_mini` - `iphone_12` - `iphone_x` - `ipad` - `galaxy_s23_ultra` - `galaxy_s23` - `galaxy_s8` - `galaxy_fold4` - `pixel_7_pro` - `pixel_6a` - `pixel_5` - `redmi_note_12_pro` - `redmi_note_11` - `huawei_p60_pro` - `huawei_mate_50_pro` |
| `full_page` | query | `boolean` | No | Capture the entire page instead of just the visible viewport. |
| `full_page_scroll` | query | `boolean` | No | Scroll the page to fully load lazy-loaded elements before capturing a full-page screenshot. |
| `full_page_scroll_duration` | query | `number` | No | Time in milliseconds for scrolling before capturing the full page. |
| `viewport_width` | query | `number` | No | The width of the browser viewport in pixels. |
| `viewport_height` | query | `number` | No | The height of the browser viewport in pixels. |
| `scale_factor` | query | `number` | No | The scale factor for high-resolution screenshots. |
| `image_quality` | query | `string` | No | Generate images with custom quality settings. Supported formats include: ``` jpeg ``` ``` webp ``` |
| `selector` | query | `string` | No | Capture a specific element on the page instead of the full viewport. |
| `cache` | query | `boolean` | No | Cache the response. |
| `cache_ttl` | query | `number` | No | Cache the response for a custom TTL (in seconds). |
| `remove_selectors` | query | `string` | No | A comma-separated list of elements to hide before capturing (e.g., ads, popups). |
| `remove_cookie_banners` | query | `boolean` | No | Automatically remove cookie banners before capturing. |
| `remove_ads` | query | `boolean` | No | Automatically remove ads before capturing. |
| `block_resources` | query | `string` (`document`, `eventsource`, `fetch`, `font`, `image`, `manifest`, `media`, `other`, `script`, `stylesheet`, `texttrack`, `websocket`, `xhr`) | No | Comma-separated list of resource types to block (e.g., “image,stylesheet,font”). Available resource types: - `document` - `stylesheet` - `image` - `media` - `font` - `script` - `texttrack` - `xhr` - `fetch` - `eventsource` - `websocket` - `manifest` - `other` Useful for optimizing page loading speed before capturing screenshots. |
| `block_urls` | query | `string` | No | Comma-separated list of URL patterns to block (e.g., “analytics,tracking,advertisement”). You can specify URLs, domains, or simple patterns like “.example.com/”. |
| `delay` | query | `number` | No | Delay in seconds before capturing the screenshot. |
| `wait_until` | query | `array` (`domcontentloaded`, `load`, `networkidle0`, `networkidle2`) | No | Define when to capture. Available formats: - `networkidle2` - `load` - `domcontentloaded` - `networkidle0` |
| `wait_for_selector` | query | `string` | No | Wait for a specific element to appear before taking the screenshot. |
| `proxy` | query | `string` | No | Specify a proxy server to route your request through. Supports `HTTP`, `HTTPS`, and `SOCKS5` proxies. Format: http://username:password@proxy.com:PORT. Useful for bypassing geo-restrictions and rotating IPs. |
| `s3_secret_key` | query | `string` | No | AWS S3 secret access key. |
| `s3_access_key_id` | query | `string` | No | AWS S3 access key ID. |
| `s3_bucket` | query | `string` | No | The S3 bucket name where the screenshot will be uploaded. |
| `s3_object_key` | query | `string` | No | The filename for the S3 object (auto-generated if not provided). |
| `s3_region` | query | `string` | No | AWS S3 region for storage. |
| `storage_endpoint` | query | `string` | No | Leave this field empty for AWS S3. Only specify a value if required. S3-compatible storage services are supported, for example, use https://.r2.cloudflarestorage.com for Cloudflare R2. |
| `s3_url` | query | `boolean` | No | Use S3 storage for screenshots. The S3 URL will be returned in the response. |
## Responses
### 200 Successful response
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website Technology (/website-technology)
**GET** `https://api.piloterr.com/v2/website/technology`
Identify the technologies behind any website CMS, frameworks, analytics, CDN, hosting, and more, for competitive market analysis and technological insight.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A website URL to analyze for technology stack detection. Must include the `http://` or `https://` protocol. **Example:** https://example.com **Notes:** - Response time is typically between 10 and 15 seconds due to deep technology fingerprinting. - Works with any public-facing website URL. |
| `mode` | query | `string` | No | Controls the level of detail returned in the technology detection response. - `expert`: Returns comprehensive technical details including confidence scores, version numbers, CPE identifiers, category metadata, and technology descriptions. **Default.** - `simple`: Returns a lightweight summary with the CMS name, URL status codes, and a flat list of technology names **Notes:** - Use `simple` mode for quick stack overviews or when building dashboards that only need technology names. - Use `expert` mode when you need confidence scores, categorization, or version information for deep analysis. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"cms": {
"type": "string"
},
"urls": {
"type": "object",
"properties": {
"https://piloterr.com/": {
"type": "object",
"properties": {
"status": {
"type": "integer"
}
}
},
"https://www.piloterr.com/": {
"type": "object",
"properties": {
"status": {
"type": "integer"
}
}
}
}
},
"technologies": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Website WebUnlocker (/website-webunlocker)
**GET** `https://api.piloterr.com/v2/website/webunlocker`
Bypass advanced anti-bot systems (Cloudflare, DataDome, Akamai, PerimeterX) on whitelisted domains. Combines browser rendering and unlock tooling; use after Crawler and Rendering fail.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A public website URL to unlock with the WebUnlocker engine. Must include `http://` or `https://`. The target domain must be on Piloterr's approved whitelist. **Example:** https://www.norauto.fr/t/pneu/w-185-h-55-r-14/ete-s/80-l-h-q.html **Choosing the right engine:** - Try [Website Crawler](https://www.piloterr.com/library/website-crawler) and [Website Rendering](https://www.piloterr.com/library/website-rendering) first when possible (lower cost). - Use WebUnlocker only for advanced bot protection on approved domains. - Contact Piloterr support to request whitelist access before production. See [Website Scraping guide](https://www.piloterr.com/blog/website-crawler-vs-rendering-vs-webunlocker) for a full comparison. |
| `allow_redirects` | query | `boolean` | No | Controls whether the WebUnlocker engine should follow HTTP redirects. - `true`: Follow redirects automatically (**default**) - `false`: Stop at the first HTTP response without following any redirect **Notes:** - **Set to `false`** when you need to inspect the redirect chain or capture intermediate responses. |
| `return_page_source` | query | `boolean` | No | Controls whether the response returns the raw, unprocessed HTML source of the page. - `false`: Return processed/parsed content (**default**) - `true`: Return the exact HTML source as delivered by the web server after bot bypass **Notes:** - Useful when you need the unmodified server response for debugging or precise parsing. |
## Responses
### 200 Successful response
```json
{
"type": "string"
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Weibo Post Info (/weibo-post-info)
**GET** `https://api.piloterr.com/v2/weibo/post/info`
Retrieve detailed information from any Weibo post, including post text, images, user profile data, and engagement metrics.
**Status:** Degraded
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Weibo post URL or post ID. **Examples:** https://m.weibo.cn/detail/5062595839264600 Both the full Weibo mobile URL and the numeric post ID are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"post": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"pics": {
"type": "array",
"items": {
"type": "object",
"properties": {
"pid": {
"type": "string"
},
"url": {
"type": "string"
},
"large": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
}
}
}
}
},
"text": {
"type": "string"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"svip": {
"type": "integer"
},
"verified": {
"type": "boolean"
},
"enterprise": {
"type": "integer"
},
"screen_name": {
"type": "string"
},
"followers_count": {
"type": "string"
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Weibo Search (/weibo-search)
**GET** `https://api.piloterr.com/v2/weibo/search`
Search Weibo using a mobile search URL to retrieve posts, users, and content matching a keyword or hashtag.
**Status:** Degraded
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Weibo search URL. **Example:** https://m.weibo.cn/search?containerid=231522type%3D1%26t%3D10%26q%3D%23%E8%B0%A2%E8%B0%A2%E4%BD%A0%E4%BB%AC%23 Generate search URLs from the Weibo mobile search interface (m.weibo.cn) and copy the URL. |
| `page` | query | `number` | No | Page number for paginating through search results. **Note:** Increment the page number to retrieve subsequent pages of results. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"response": {
"type": "array",
"items": {
"type": "object",
"properties": {
"card_type": {
"type": "integer"
},
"show_type": {
"type": "integer"
},
"card_group": {
"type": "array",
"items": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"gender": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"profile_url": {
"type": "string"
},
"screen_name": {
"type": "string"
},
"follow_count": {
"type": "integer"
},
"statuses_count": {
"type": "integer"
},
"followers_count": {
"type": "string"
},
"profile_image_url": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Wellfound Company Info (/wellfound-company-info)
**GET** `https://api.piloterr.com/v2/wellfound/company/info`
Retrieve startup and company data from Wellfound (formerly AngelList), including description, funding, team size, hiring status, and social links.
**Status:** Degraded
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | A Wellfound company URL or company slug. **Examples:** https://wellfound.com/company/dataiku Both the full Wellfound URL and the company slug are accepted. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"hiring": {
"type": "boolean"
},
"website": {
"type": "string"
},
"headline": {
"type": "string"
},
"description": {
"type": "string"
},
"is_incubator": {
"type": "boolean"
},
"is_operating": {
"nullable": true
},
"employee_count": {
"type": "string"
},
"social_networks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"company_financials_highlights": {
"type": "object",
"properties": {
"funding_total": {
"type": "integer"
},
"num_funding_rounds": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# X Post Info (twitter) (/x-post-info)
**GET** `https://api.piloterr.com/v2/x/post/info`
X Post Info API: fetch public tweet data (text, likes, retweets, views, media) from a status URL or tweet ID via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | X tweet to retrieve. Accepts a full status URL or numeric tweet ID. **Examples:** ``` https://x.com/rauchg/status/2070215849970119090 ``` ``` 2070215849970119090 ``` **Notes:** - Legacy `twitter.com` status URLs are supported. - `created_at` and `name` may be null when not shown on the rendered page. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"nullable": true
},
"text": {
"type": "string"
},
"media": {
"type": "array",
"items": {
"type": "string"
}
},
"tweet_id": {
"type": "string"
},
"username": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"created_at": {
"nullable": true
},
"like_count": {
"type": "integer"
},
"view_count": {
"type": "integer"
},
"reply_count": {
"type": "integer"
},
"retweet_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# X Profile Info (twitter) (/x-profile-info)
**GET** `https://api.piloterr.com/v2/x/profile/info`
X Profile Info API: fetch public X/Twitter profile data (bio, followers, verification, avatars) from a username or URL via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | X profile to look up. Accepts a username, @handle, or profile URL. **Examples:** ``` elonmusk ``` ``` @elonmusk ``` ``` https://x.com/elonmusk ``` **Notes:** - Legacy `twitter.com` URLs are supported. - **Rejected (400):** search URLs, status/tweet URLs, invalid handles. - X Search is not supported by this API. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"user_id": {
"type": "string"
},
"location": {
"nullable": true
},
"username": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"created_at": {
"type": "string"
},
"description": {
"type": "string"
},
"tweet_count": {
"type": "integer"
},
"followers_count": {
"type": "integer"
},
"following_count": {
"type": "integer"
},
"profile_image_url": {
"type": "string"
},
"profile_banner_url": {
"type": "string"
},
"profile_image_url_normal": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Yellowpages Business (/yellowpages-business)
**GET** `https://api.piloterr.com/v2/yellowpages/business`
Extract a full Yellow Pages business profile from a listing URL, including phone, website, categories, and ratings via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Yellow Pages business listing URL. **Example:** https://www.yellowpages.com/los-angeles-ca/mip/anawalt-lumber-13945594?lid=1002108313121 **Notes:** - Only `/mip/` business profile URLs on yellowpages.com are accepted. - Query parameters such as `?lid=` are supported. - Business profile pages are JavaScript SPAs; this endpoint uses website rendering to extract phone, website, categories, and ratings. - Copy the listing URL from search results or from yellowpages.com. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"rating": {
"type": "string"
},
"address": {
"type": "string"
},
"website": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "string"
}
},
"rating_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Yellowpages Search (/yellowpages-search)
**GET** `https://api.piloterr.com/v2/yellowpages/search`
Search Yellow Pages businesses from a full search URL and retrieve names, phones, addresses, ratings, opening hours, and pagination via browser rendering.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Yellow Pages search URL. **Example 1 (category + city):** https://www.yellowpages.com/los-angeles-ca/plumbers **Example 2 (page 2):** https://www.yellowpages.com/los-angeles-ca/plumbers?page=2 **Notes:** - Only `yellowpages.com` search listing URLs are accepted. - Copy the full URL from yellowpages.com, including city slug, category, and `?page=` for pagination. - Yellow Pages search pages are JavaScript SPAs; this endpoint uses website rendering to scrape business cards. - Pass each result's `url` to the Yellowpages Business endpoint for full profile data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"rating_value": {
"type": "number"
},
"review_count": {
"type": "integer"
},
"opening_hours": {
"type": "array",
"items": {
"type": "string"
}
},
"address_region": {
"type": "string"
},
"street_address": {
"type": "string"
},
"address_country": {
"type": "string"
},
"address_locality": {
"type": "string"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"next_url": {
"type": "string"
},
"per_page": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Yelp Business (/yelp-business)
**GET** `https://api.piloterr.com/v2/yelp/business`
Fetch a Yelp business profile from a full business URL with phone, address, website, logo, hours, and claim status.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Yelp business URL. **Example:** https://www.yelp.com/biz/novela-san-francisco-2 Use an organic `/biz/` link from **Yelp Search** results. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"address": {
"type": "string"
},
"website": {
"type": "string"
},
"open_hours": {
"type": "object",
"properties": {
"fri": {
"type": "string"
},
"mon": {
"type": "string"
},
"sat": {
"type": "string"
},
"sun": {
"type": "string"
},
"thu": {
"type": "string"
},
"tue": {
"type": "string"
},
"wed": {
"type": "string"
}
}
},
"business_id": {
"type": "string"
},
"claim_status": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Yelp Reviews (/yelp-reviews)
**GET** `https://api.piloterr.com/v2/yelp/reviews`
Fetch paginated Yelp reviews for a business with rating, text, author profile, reactions, and attached photos or videos.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Yelp business ID / encid. **Example:** cX5dOMkwZ5rqWbeIJpw_Ig Use `biz_id` from **Yelp Search** or `business_id` from **Yelp Business**. |
| `page` | query | `number` | No | Page number. Each page returns up to 10 reviews. Default: `1`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "object",
"properties": {
"full": {
"type": "string"
},
"language": {
"type": "string"
}
}
},
"encid": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"encid": {
"type": "string"
},
"display_name": {
"type": "string"
},
"friend_count": {
"type": "integer"
},
"review_count": {
"type": "integer"
},
"display_location": {
"type": "string"
},
"business_photo_count": {
"type": "integer"
}
}
},
"rating": {
"type": "integer"
},
"business": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"alias": {
"type": "string"
},
"encid": {
"type": "string"
}
}
},
"feedback": {
"type": "object",
"properties": {
"cool_count": {
"nullable": true
},
"funny_count": {
"nullable": true
},
"useful_count": {
"nullable": true
}
}
},
"created_at": {
"nullable": true
},
"business_photos": {
"type": "array",
"items": {}
},
"business_videos": {
"type": "array",
"items": {}
},
"available_reactions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"display_text": {
"type": "string"
},
"reaction_type": {
"type": "string"
}
}
}
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
},
"business_id": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Yelp Search (/yelp-search)
**GET** `https://api.piloterr.com/v2/yelp/search`
Search Yelp by keyword and location and retrieve paginated business cards with ratings, categories, photos, and sponsored flags.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Yelp search URL with `find_desc` and `find_loc` query parameters. **Example:** https://www.yelp.com/search?find_desc=Cocktail+Bars&find_loc=San+Francisco%2C+CA Use organic `results[].link` values on **Yelp Business**, or `results[].biz_id` on **Yelp Reviews**. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"name": {
"type": "string"
},
"alias": {
"type": "string"
},
"is_ad": {
"type": "boolean"
},
"phone": {
"type": "string"
},
"biz_id": {
"type": "string"
},
"rating": {
"type": "integer"
},
"picture": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "string"
}
},
"price_range": {
"type": "string"
},
"review_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {
"type": "integer"
}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zillow Property (/zillow-property)
**GET** `https://api.piloterr.com/v2/zillow/property`
Extract full property details from a Zillow homedetails URL, including specs, photos, schools, tax history, and agent attribution.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Zillow homedetails URL. **Example:** https://www.zillow.com/homedetails/20-Kyle-Ct-Staten-Island-NY-10312/32361983_zpid/ **Notes:** - Only Zillow homedetails URLs are accepted (`/homedetails/.../{zpid}_zpid/`). - Copy the `detail_url` value returned by the Zillow Search endpoint. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"city": {
"type": "string"
},
"zpid": {
"type": "integer"
},
"mlsid": {
"type": "string"
},
"price": {
"type": "integer"
},
"state": {
"type": "string"
},
"county": {
"type": "string"
},
"photos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"caption": {
"nullable": true
},
"subject_type": {
"nullable": true
},
"mixed_sources": {
"type": "object",
"properties": {
"jpeg": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
},
"webp": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
}
}
}
}
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"zipcode": {
"type": "string"
},
"community": {
"nullable": true
},
"subdivision": {
"type": "string"
},
"neighborhood": {
"nullable": true
},
"street_address": {
"type": "string"
}
}
},
"country": {
"type": "string"
},
"hdp_url": {
"type": "string"
},
"pals_id": {
"type": "string"
},
"schools": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"name": {
"type": "string"
},
"grades": {
"type": "string"
},
"rating": {
"type": "integer"
},
"distance": {
"type": "number"
}
}
}
},
"zipcode": {
"type": "string"
},
"bedrooms": {
"type": "integer"
},
"currency": {
"type": "string"
},
"latitude": {
"type": "number"
},
"lot_size": {
"type": "integer"
},
"bathrooms": {
"type": "integer"
},
"home_type": {
"type": "string"
},
"longitude": {
"type": "number"
},
"zestimate": {
"type": "integer"
},
"reso_facts": {
"type": "object",
"properties": {
"cooling": {
"type": "array",
"items": {
"type": "string"
}
},
"heating": {
"type": "array",
"items": {
"type": "string"
}
},
"stories": {
"nullable": true
},
"basement": {
"type": "string"
},
"bedrooms": {
"type": "integer"
},
"lot_size": {
"type": "string"
},
"bathrooms": {
"type": "integer"
},
"has_garage": {
"type": "boolean"
},
"year_built": {
"type": "integer"
},
"living_area": {
"type": "string"
},
"parking_features": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"year_built": {
"type": "integer"
},
"description": {
"type": "string"
},
"home_status": {
"type": "string"
},
"living_area": {
"type": "integer"
},
"photo_count": {
"type": "integer"
},
"tax_history": {
"type": "array",
"items": {
"type": "object",
"properties": {
"time": {
"type": "integer"
},
"value": {
"type": "integer"
},
"tax_paid": {
"type": "integer"
},
"tax_increase_rate": {
"type": "number"
},
"value_increase_rate": {
"type": "number"
}
}
}
},
"has_vr_model": {
"type": "boolean"
},
"parent_region": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"price_history": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"time": {
"type": "integer"
},
"event": {
"type": "string"
},
"price": {
"type": "integer"
},
"source": {
"type": "string"
},
"buyer_agent": {
"nullable": true
},
"seller_agent": {
"nullable": true
},
"attribute_source": {
"type": "object",
"properties": {
"info_string1": {
"type": "string"
},
"info_string2": {
"type": "string"
},
"info_string3": {
"type": "string"
}
}
},
"show_county_link": {
"type": "boolean"
},
"posting_is_rental": {
"type": "boolean"
},
"price_change_rate": {
"type": "integer"
},
"price_per_square_foot": {
"type": "integer"
}
}
}
},
"brokerage_name": {
"type": "string"
},
"days_on_zillow": {
"type": "integer"
},
"favorite_count": {
"type": "integer"
},
"lot_area_units": {
"type": "string"
},
"lot_area_value": {
"type": "integer"
},
"rent_zestimate": {
"type": "integer"
},
"street_address": {
"type": "string"
},
"time_on_zillow": {
"type": "string"
},
"page_view_count": {
"type": "integer"
},
"assigned_schools": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"level": {
"type": "string"
},
"grades": {
"type": "string"
},
"rating": {
"type": "integer"
},
"distance": {
"type": "number"
},
"school_id": {
"type": "integer"
},
"enrollment": {
"type": "integer"
},
"district_id": {
"type": "integer"
},
"district_name": {
"type": "string"
},
"themed_ratings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"rating": {
"type": "integer"
}
}
}
},
"student_teacher_ratio": {
"type": "number"
},
"student_counselor_ratio": {
"type": "integer"
},
"percentage_of_full_time_teachers_who_are_certified": {
"type": "integer"
}
}
}
},
"attribution_info": {
"type": "object",
"properties": {
"mls_id": {
"type": "string"
},
"mls_name": {
"type": "string"
},
"agent_name": {
"type": "string"
},
"broker_name": {
"type": "string"
},
"listing_agents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"member_full_name": {
"type": "string"
},
"member_state_license": {
"nullable": true
},
"associated_agent_type": {
"type": "string"
}
}
}
},
"listing_offices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"office_name": {
"type": "string"
},
"associated_office_type": {
"type": "string"
}
}
}
},
"attribution_title": {
"type": "string"
}
}
},
"listing_sub_type": {
"type": "object",
"properties": {
"is_fsba": {
"type": "boolean"
},
"is_fsbo": {
"type": "boolean"
},
"is_pending": {
"type": "boolean"
},
"is_new_home": {
"type": "boolean"
},
"is_bank_owned": {
"type": "boolean"
},
"is_open_house": {
"type": "boolean"
},
"is_coming_soon": {
"type": "boolean"
},
"is_for_auction": {
"type": "boolean"
},
"is_foreclosure": {
"type": "boolean"
}
}
},
"hi_res_image_link": {
"type": "string"
},
"living_area_units": {
"type": "string"
},
"living_area_value": {
"type": "integer"
},
"abbreviated_address": {
"type": "string"
},
"is_showcase_listing": {
"type": "boolean"
},
"neighborhood_region": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"listing_type_dimension": {
"type": "string"
},
"property_type_dimension": {
"type": "string"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zillow Search (/zillow-search)
**GET** `https://api.piloterr.com/v2/zillow/search`
Search Zillow listings from a search or map URL and retrieve prices, beds, baths, coordinates, and listing status.
**Credit cost:** 3 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Zillow search or map results URL. **Example 1 (city search URL):** https://www.zillow.com/staten-island-ny/ **Example 2 (map search URL):** https://www.zillow.com/homes/for_sale/Staten-Island-NY_rb/ **Notes:** - Only Zillow URLs are accepted (search pages, map results, filtered browse URLs). - Pagination and filter parameters present in the URL are respected. - Use each result's `detail_url` with the Zillow Property endpoint. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"area": {
"type": "integer"
},
"beds": {
"type": "integer"
},
"list": {
"type": "boolean"
},
"zpid": {
"type": "string"
},
"baths": {
"type": "integer"
},
"pgapt": {
"type": "string"
},
"price": {
"type": "string"
},
"sgapt": {
"type": "string"
},
"address": {
"type": "string"
},
"img_src": {
"type": "string"
},
"pals_id": {
"type": "string"
},
"lat_long": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
}
},
"has_image": {
"type": "boolean"
},
"zestimate": {
"type": "integer"
},
"detail_url": {
"type": "string"
},
"broker_name": {
"type": "string"
},
"status_text": {
"type": "string"
},
"status_type": {
"type": "string"
},
"address_city": {
"type": "string"
},
"has3_d_model": {
"type": "boolean"
},
"address_state": {
"type": "string"
},
"address_street": {
"type": "string"
},
"address_zipcode": {
"type": "string"
},
"country_currency": {
"type": "string"
},
"unformatted_price": {
"type": "integer"
},
"raw_home_status_cd": {
"type": "string"
},
"is_showcase_listing": {
"type": "boolean"
},
"marketing_status_simplified_cd": {
"type": "string"
}
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zoopla Property (/zoopla-property)
**GET** `https://api.piloterr.com/v2/zoopla/property`
Extract full property details from a Zoopla listing URL, including price, specs, photo gallery, EPC rating, coordinates, and agent data.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Zoopla property detail URL. **Example:** https://www.zoopla.co.uk/to-rent/details/73119609/ **Notes:** - Only Zoopla `/details/` listing URLs are accepted. - Copy the listing URL from search results or from zoopla.co.uk. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"url": {
"type": "string"
},
"agent": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"logo": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"price": {
"type": "object",
"properties": {
"amount": {
"type": "integer"
},
"currency": {
"type": "string"
}
}
},
"title": {
"type": "string"
},
"address": {
"type": "string"
},
"gallery": {
"type": "array",
"items": {
"type": "string"
}
},
"epc_rating": {
"type": "string"
},
"floor_area": {
"type": "string"
},
"coordinates": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"google_mape_source": {
"type": "string"
}
}
},
"property_info": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
},
"property_tags": {
"type": "array",
"items": {
"type": "string"
}
},
"num_of_bedrooms": {
"type": "integer"
},
"num_of_bathrooms": {
"type": "integer"
},
"num_of_receptions": {
"type": "integer"
},
"property_description": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zoopla Search (/zoopla-search)
**GET** `https://api.piloterr.com/v2/zoopla/search`
Search Zoopla listings from a search URL and retrieve prices, specs, photos, agency info, and pagination.
**Credit cost:** 2 credits per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Full Zoopla search results URL. **Example 1 (for-sale):** https://www.zoopla.co.uk/for-sale/property/manchester/ **Example 2 (filtered search):** https://www.zoopla.co.uk/for-sale/property/manchester/?beds_min=2&price_max=160000&q=Manchester%2C%20Greater%20Manchester **Example 3 (to-rent):** https://www.zoopla.co.uk/to-rent/property/london/ **Notes:** - Supports for-sale, to-rent, and new-homes search URLs. - Filters and pagination parameters in the URL are respected. - Pagination via `?pn=2` in the URL. - Use each result's `url` with the Zoopla Property endpoint for full listing data. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"image": {
"type": "string"
},
"price": {
"type": "integer"
},
"agency": {
"type": "string"
},
"address": {
"type": "string"
},
"square_ft": {
"type": "integer"
},
"just_added": {
"type": "boolean"
},
"description": {
"type": "string"
},
"num_bedrooms": {
"type": "integer"
},
"num_bathrooms": {
"type": "integer"
},
"price_currency": {
"type": "string"
},
"num_living_room": {
"nullable": true
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "integer"
},
"current": {
"type": "integer"
},
"other_pages": {
"type": "array",
"items": {}
},
"total_pages": {
"type": "integer"
},
"has_next_page": {
"type": "boolean"
},
"total_results": {
"type": "integer"
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zooplus Product (/zooplus-product)
**GET** `https://api.piloterr.com/v2/zooplus/product`
Zooplus Product API. Fetch full pet product page data from a Zooplus URL or shopIdentifier. Returns pricing, images, description, brand, ratings and availability.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Zooplus product URL or `shopIdentifier`. **Examples:** ``` 620472 ``` ``` https://www.zooplus.fr/shop/chats/boites_sachets_barquettes_chat/felix_boites_chat/tendres_effiles/620472 ``` Numeric id resolves via internal search on the target shop when `region` is set. |
| `region` | query | `string` | No | Target country when `query` is a `shopIdentifier` only (default: `fr`). Ignored for full product URLs. |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"brand": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
},
"summary": {
"type": "string"
},
"category": {
"type": "string"
},
"currency": {
"type": "string"
},
"in_stock": {
"type": "boolean"
},
"image_url": {
"type": "string"
},
"product_id": {
"type": "string"
},
"description": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"variant_count": {
"type": "integer"
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Zooplus Search (/zooplus-search)
**GET** `https://api.piloterr.com/v2/zooplus/search`
Zooplus Search API. Search pet food and supplies by keyword or URL across 26 Zooplus/Zoohit shops. Parses Next.js RSC initialStoreState; returns prices, ratings and pagination.
**Credit cost:** 1 credit per call
## Authentication
Pass your API key as a header:
```
x-api-key: YOUR_API_KEY
```
## Parameters
| Name | In | Type | Required | Description |
|------|----|------|:--------:|-------------|
| `query` | query | `string` | Yes | Search keyword or full Zooplus search URL. **Examples:** ``` croquettes chat ``` ``` https://www.zooplus.fr/search/results?q=croquettes+chat ``` Use **Zooplus Product** on any result `url` or `product_id` for full PDP data. |
| `region` | query | `string` | No | Target country when `query` is a keyword (default: `fr`). **Examples:** `de`, `gb`, `pl`, `ch-fr` Ignored when `query` is a full Zooplus URL. |
| `page` | query | `number` | No | 1-based page number override (default: read from URL or `1`). Zooplus paginates with `p` (`?q=...&p=2`). |
| `return_page_source` | query | `boolean` | No | When `true`, return the raw HTML page source as `{"result": "..."}` instead of structured JSON. Defaults to `false`. |
## Responses
### 200 Successful response
```json
{
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"price": {
"type": "number"
},
"title": {
"type": "string"
},
"rating": {
"type": "integer"
},
"summary": {
"type": "string"
},
"currency": {
"type": "string"
},
"image_url": {
"type": "string"
},
"is_on_sale": {
"type": "boolean"
},
"product_id": {
"type": "string"
},
"review_count": {
"type": "integer"
},
"variant_count": {
"type": "integer"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"total_results": {
"nullable": true
}
}
}
}
}
```
### 400 Bad Request — missing or invalid parameters.
```json
{
"error": "Bad Request"
}
```
### 401 Unauthorized — the API key is missing, invalid, inactive, or expired.
```json
{
"error": "Invalid API Key"
}
```
### 402 Payment Required — your subscription or credit balance is insufficient.
```json
{
"error": "Payment required"
}
```
### 429 Too Many Requests — you exceeded your rate limit quota.
```json
{
"error": "Rate limit exceeded for the API key: quota monthly"
}
```
### 500 Internal Server Error — something went wrong on our side.
```json
{
"error": "Internal Error"
}
```
# Collect Google Images (/playbooks/autom-google-images)
## Overview [#overview]
This playbook shows how to collect image URLs, titles, and dimensions from Google Images for a given query. Each result includes the direct image URL, the page it was found on, its source domain, and pixel dimensions.
## Prerequisites [#prerequisites]
* An Autom API key — get one at [app.autom.dev](https://app.autom.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default in most PHP installs).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Search for images [#search-for-images]
Call `GET /v1/google/images` with a `q` parameter.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.autom.dev/v1/google/images",
headers={"x-api-key": API_KEY},
params={"query": "electric car charging station", "gl": "us", "hl": "en"},
)
data = response.json()
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ q: "electric car charging station", gl: "us", hl: "en" });
const response = await fetch(`https://api.autom.dev/v1/google/images?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
```
```php
"electric car charging station", "gl" => "us", "hl" => "en"]);
$ch = curl_init("https://api.autom.dev/v1/google/images?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
```
```go
package main
import (
"encoding/json"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"query": {"electric car charging station"}, "gl": {"us"}, "hl": {"en"}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/images?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.autom.dev/v1/google/images?q=electric+car+charging+station&gl=us&hl=en"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync(
"https://api.autom.dev/v1/google/images?q=electric+car+charging+station&gl=us&hl=en");
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.autom.dev/v1/google/images")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("query", "electric car charging station"), ("gl", "us"), ("hl", "en")])
.send().await?.json::().await?;
println!("{:#?}", data);
Ok(())
}
```
### Inspect the image results [#inspect-the-image-results]
Each item in `images` has `url` (direct image), `link` (source page), `title`, `domain`, `source`, `image_width`, and `image_height`.
```python
for img in data.get("images", []):
print(f"[{img['position']}] {img['title']}")
print(f" Image : {img['url']}")
print(f" Source: {img['source']} — {img['image_width']}x{img['image_height']}px\n")
```
```typescript
for (const img of data.images ?? []) {
console.log(`[${img.position}] ${img.title}`);
console.log(` Image : ${img.url}`);
console.log(` Source: ${img.source} — ${img.image_width}x${img.image_height}px\n`);
}
```
```php
foreach ($data["images"] ?? [] as $img) {
echo "[{$img['position']}] {$img['title']}\n";
echo " Image : {$img['url']}\n";
echo " Source: {$img['source']} — {$img['image_width']}x{$img['image_height']}px\n\n";
}
```
```go
import "fmt"
for _, r := range data["images"].([]any) {
img := r.(map[string]any)
fmt.Printf("[%.0f] %s\n Image : %s\n Source: %s — %.0fx%.0fpx\n\n",
img["position"], img["title"], img["url"],
img["source"], img["image_width"], img["image_height"])
}
```
```java
import org.json.*;
var images = new JSONObject(response.body()).getJSONArray("images");
for (int i = 0; i < images.length(); i++) {
var img = images.getJSONObject(i);
System.out.printf("[%d] %s%n Image : %s%n Source: %s — %dx%dpx%n%n",
img.getInt("position"), img.getString("title"),
img.getString("url"), img.getString("source"),
img.getInt("image_width"), img.getInt("image_height"));
}
```
```csharp
using System.Text.Json;
var images = JsonDocument.Parse(body).RootElement.GetProperty("images").EnumerateArray();
foreach (var img in images)
{
Console.WriteLine($"[{img.GetProperty("position")}] {img.GetProperty("title")}");
Console.WriteLine($" Image : {img.GetProperty("url")}");
Console.WriteLine($" Source: {img.GetProperty("source")} — {img.GetProperty("image_width")}x{img.GetProperty("image_height")}px\n");
}
```
```rust
if let Some(images) = data["images"].as_array() {
for img in images {
println!("[{}] {}", img["position"], img["title"].as_str().unwrap_or(""));
println!(" Image : {}", img["url"].as_str().unwrap_or(""));
println!(" Source: {} — {}x{}px\n",
img["source"].as_str().unwrap_or(""), img["image_width"], img["image_height"]);
}
}
```
### Build an image catalog filtered by minimum size [#build-an-image-catalog-filtered-by-minimum-size]
Filter out thumbnails and save only high-resolution images.
```python
import csv, requests
API_KEY = "YOUR_API_KEY"
QUERY = "electric car charging station"
MIN_WIDTH = 800
def fetch_images(query: str, pages: int = 2) -> list:
results = []
for page in range(1, pages + 1):
r = requests.get("https://api.autom.dev/v1/google/images",
headers={"x-api-key": API_KEY},
params={"query": query, "gl": "us", "hl": "en", "page": page})
results.extend(r.json().get("images", []))
return results
large = [img for img in fetch_images(QUERY) if img.get("image_width", 0) >= MIN_WIDTH]
with open("image_catalog.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["position", "title", "url", "source", "image_width", "image_height"])
writer.writeheader()
writer.writerows(large)
print(f"Saved {len(large)} high-res images to image_catalog.csv")
```
```typescript
import { writeFileSync } from "fs";
const API_KEY = "YOUR_API_KEY";
const MIN_WIDTH = 800;
async function fetchImages(query: string, pages = 2): Promise {
const all: any[] = [];
for (let page = 1; page <= pages; page++) {
const params = new URLSearchParams({ q: query, gl: "us", hl: "en", page: String(page) });
const res = await fetch(`https://api.autom.dev/v1/google/images?${params}`, {
headers: { "x-api-key": API_KEY },
});
all.push(...((await res.json()).images ?? []));
}
return all;
}
const images = await fetchImages("electric car charging station");
const large = images.filter(img => (img.image_width ?? 0) >= MIN_WIDTH);
writeFileSync("image_catalog.json", JSON.stringify(large, null, 2));
console.log(`Saved ${large.length} high-res images to image_catalog.json`);
```
```php
$query, "gl" => "us", "hl" => "en", "page" => $page]);
$ch = curl_init("https://api.autom.dev/v1/google/images?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$all = array_merge($all, $data["images"] ?? []);
}
return $all;
}
$large = array_filter(fetchImages($apiKey, $query), fn($img) => ($img["image_width"] ?? 0) >= $minWidth);
file_put_contents("image_catalog.json", json_encode(array_values($large), JSON_PRETTY_PRINT));
echo "Saved " . count($large) . " high-res images to image_catalog.json\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
)
func fetchImages(apiKey, query string, pages int) []map[string]any {
var all []map[string]any
for page := 1; page <= pages; page++ {
params := url.Values{"query": {query}, "gl": {"us"}, "hl": {"en"}, "page": {strconv.Itoa(page)}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/images?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var data map[string]any
json.Unmarshal(body, &data)
for _, r := range data["images"].([]any) {
all = append(all, r.(map[string]any))
}
}
return all
}
func main() {
images := fetchImages("YOUR_API_KEY", "electric car charging station", 2)
minWidth := float64(800)
var large []map[string]any
for _, img := range images {
if img["image_width"].(float64) >= minWidth {
large = append(large, img)
}
}
b, _ := json.MarshalIndent(large, "", " ")
os.WriteFile("image_catalog.json", b, 0644)
fmt.Printf("Saved %d high-res images to image_catalog.json\n", len(large))
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
public static void main(String[] args) throws Exception {
var apiKey = "YOUR_API_KEY";
var query = URLEncoder.encode("electric car charging station", StandardCharsets.UTF_8);
var minWidth = 800;
var client = HttpClient.newHttpClient();
var all = new JSONArray();
for (int page = 1; page <= 2; page++) {
var url = "https://api.autom.dev/v1/google/images?q=" + query + "&gl=us&hl=en&page=" + page;
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var imgs = new JSONObject(resp.body()).getJSONArray("images");
for (int i = 0; i < imgs.length(); i++) all.put(imgs.get(i));
}
var large = new JSONArray();
for (int i = 0; i < all.length(); i++) {
var img = all.getJSONObject(i);
if (img.getInt("image_width") >= minWidth) large.put(img);
}
Files.writeString(Path.of("image_catalog.json"), large.toString(2));
System.out.println("Saved " + large.length() + " high-res images to image_catalog.json");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var minWidth = 800;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var all = new List();
for (int page = 1; page <= 2; page++)
{
var body = await client.GetStringAsync(
$"https://api.autom.dev/v1/google/images?q=electric+car+charging+station&gl=us&hl=en&page={page}");
var json = JsonDocument.Parse(body).RootElement;
foreach (var img in json.GetProperty("images").EnumerateArray())
all.Add(img);
}
var large = all.Where(img => img.GetProperty("image_width").GetInt32() >= minWidth).ToList();
File.WriteAllText("image_catalog.json", JsonSerializer.Serialize(large, new JsonSerializerOptions { WriteIndented = true }));
Console.WriteLine($"Saved {large.Count} high-res images to image_catalog.json");
```
```rust
use reqwest::Client;
use serde_json::Value;
use std::fs;
async fn fetch_images(client: &Client, api_key: &str, query: &str, pages: u32) -> Vec {
let mut all = Vec::new();
for page in 1..=pages {
let data = client.get("https://api.autom.dev/v1/google/images")
.header("x-api-key", api_key)
.query(&[("query", query), ("gl", "us"), ("hl", "en"), ("page", &page.to_string())])
.send().await.unwrap().json::().await.unwrap();
if let Some(imgs) = data["images"].as_array() { all.extend(imgs.clone()); }
}
all
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let images = fetch_images(&client, "YOUR_API_KEY", "electric car charging station", 2).await;
let large: Vec<&Value> = images.iter()
.filter(|img| img["image_width"].as_i64().unwrap_or(0) >= 800)
.collect();
fs::write("image_catalog.json", serde_json::to_string_pretty(&large).unwrap()).unwrap();
println!("Saved {} high-res images to image_catalog.json", large.len());
Ok(())
}
```
The `url` field in each result is the direct link to the image file. Always verify you have the rights to use an image before including it in a dataset or product.
# Monitor Google News (/playbooks/autom-google-news)
## Overview [#overview]
This playbook builds a **news monitoring pipeline**: query Google News for a brand name, product, or topic and collect all article titles, sources, and publication dates. Run it on a cron schedule to detect press coverage as soon as it appears.
## Prerequisites [#prerequisites]
* An Autom API key — get one at [app.autom.dev](https://app.autom.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default in most PHP installs).
No extra dependencies — uses the `net/http` standard library (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Fetch latest news articles [#fetch-latest-news-articles]
Call `GET /v1/google/news` with the keyword you want to monitor.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.autom.dev/v1/google/news",
headers={"x-api-key": API_KEY},
params={"query": "OpenAI", "gl": "us", "hl": "en"},
)
data = response.json()
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ q: "OpenAI", gl: "us", hl: "en" });
const response = await fetch(`https://api.autom.dev/v1/google/news?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
```
```php
"OpenAI", "gl" => "us", "hl" => "en"]);
$ch = curl_init("https://api.autom.dev/v1/google/news?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
```
```go
package main
import (
"encoding/json"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"query": {"OpenAI"}, "gl": {"us"}, "hl": {"en"}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/news?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
// use data below
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.autom.dev/v1/google/news?q=OpenAI&gl=us&hl=en"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync(
"https://api.autom.dev/v1/google/news?q=OpenAI&gl=us&hl=en");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.autom.dev/v1/google/news")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("query", "OpenAI"), ("gl", "us"), ("hl", "en")])
.send().await?
.json::().await?;
println!("{:#?}", data);
Ok(())
}
```
### Extract and display articles [#extract-and-display-articles]
Each item in `organic_results` has `title`, `link`, `source`, `date`, and `snippet`.
```python
for article in data.get("organic_results", []):
print(f"[{article['date']}] {article['title']}")
print(f" Source : {article['source']}")
print(f" URL : {article['link']}\n")
```
```typescript
for (const article of data.organic_results ?? []) {
console.log(`[${article.date}] ${article.title}`);
console.log(` Source : ${article.source}`);
console.log(` URL : ${article.link}\n`);
}
```
```php
foreach ($data["organic_results"] ?? [] as $article) {
echo "[{$article['date']}] {$article['title']}\n";
echo " Source : {$article['source']}\n";
echo " URL : {$article['link']}\n\n";
}
```
```go
results := data["organic_results"].([]any)
for _, r := range results {
a := r.(map[string]any)
fmt.Printf("[%s] %s\n Source : %s\n URL : %s\n\n",
a["date"], a["title"], a["source"], a["link"])
}
```
```java
import org.json.*;
var json = new JSONObject(response.body());
var results = json.getJSONArray("organic_results");
for (int i = 0; i < results.length(); i++) {
var a = results.getJSONObject(i);
System.out.printf("[%s] %s%n Source : %s%n URL : %s%n%n",
a.getString("date"), a.getString("title"),
a.getString("source"), a.getString("link"));
}
```
```csharp
using System.Text.Json;
var json = JsonDocument.Parse(body);
var results = json.RootElement.GetProperty("organic_results").EnumerateArray();
foreach (var a in results)
{
Console.WriteLine($"[{a.GetProperty("date")}] {a.GetProperty("title")}");
Console.WriteLine($" Source : {a.GetProperty("source")}");
Console.WriteLine($" URL : {a.GetProperty("link")}\n");
}
```
```rust
if let Some(articles) = data["organic_results"].as_array() {
for a in articles {
println!("[{}] {}", a["date"].as_str().unwrap_or(""), a["title"].as_str().unwrap_or(""));
println!(" Source : {}", a["source"].as_str().unwrap_or(""));
println!(" URL : {}\n", a["link"].as_str().unwrap_or(""));
}
}
```
### Build a monitoring pipeline with deduplication [#build-a-monitoring-pipeline-with-deduplication]
Store seen article URLs so repeated runs don't produce duplicate alerts.
```python
import json, requests
from pathlib import Path
API_KEY = "YOUR_API_KEY"
KEYWORDS = ["OpenAI", "Anthropic", "Mistral AI"]
SEEN_FILE = Path("seen_articles.json")
def load_seen() -> set:
return set(json.loads(SEEN_FILE.read_text())) if SEEN_FILE.exists() else set()
def save_seen(seen: set) -> None:
SEEN_FILE.write_text(json.dumps(list(seen)))
def fetch_news(query: str) -> list:
r = requests.get("https://api.autom.dev/v1/google/news",
headers={"x-api-key": API_KEY}, params={"query": query, "gl": "us", "hl": "en"})
return r.json().get("organic_results", [])
seen = load_seen()
new_articles = []
for keyword in KEYWORDS:
for article in fetch_news(keyword):
if article["link"] not in seen:
seen.add(article["link"])
new_articles.append({**article, "keyword": keyword})
save_seen(seen)
print(f"Found {len(new_articles)} new article(s):")
for a in new_articles:
print(f" [{a['keyword']}] {a['title']} — {a['source']}")
```
```typescript
import { readFileSync, writeFileSync, existsSync } from "fs";
const API_KEY = "YOUR_API_KEY";
const KEYWORDS = ["OpenAI", "Anthropic", "Mistral AI"];
const SEEN_FILE = "seen_articles.json";
function loadSeen(): Set {
return existsSync(SEEN_FILE)
? new Set(JSON.parse(readFileSync(SEEN_FILE, "utf-8")))
: new Set();
}
async function fetchNews(query: string): Promise {
const params = new URLSearchParams({ q: query, gl: "us", hl: "en" });
const res = await fetch(`https://api.autom.dev/v1/google/news?${params}`, {
headers: { "x-api-key": API_KEY },
});
return (await res.json()).organic_results ?? [];
}
const seen = loadSeen();
const newArticles: any[] = [];
for (const keyword of KEYWORDS) {
for (const article of await fetchNews(keyword)) {
if (!seen.has(article.link)) {
seen.add(article.link);
newArticles.push({ ...article, keyword });
}
}
}
writeFileSync(SEEN_FILE, JSON.stringify([...seen]));
console.log(`Found ${newArticles.length} new article(s):`);
for (const a of newArticles) console.log(` [${a.keyword}] ${a.title} — ${a.source}`);
```
```php
$keyword, "gl" => "us", "hl" => "en"]);
$ch = curl_init("https://api.autom.dev/v1/google/news?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($data["organic_results"] ?? [] as $article) {
if (!isset($seen[$article["link"]])) {
$seen[$article["link"]] = true;
$newArticles[] = array_merge($article, ["keyword" => $keyword]);
}
}
}
file_put_contents($seenFile, json_encode(array_keys($seen)));
echo "Found " . count($newArticles) . " new article(s):\n";
foreach ($newArticles as $a) echo " [{$a['keyword']}] {$a['title']} — {$a['source']}\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func fetchNews(apiKey, query string) []map[string]any {
params := url.Values{"query": {query}, "gl": {"us"}, "hl": {"en"}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/news?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
var out []map[string]any
for _, r := range data["organic_results"].([]any) {
out = append(out, r.(map[string]any))
}
return out
}
func main() {
apiKey := "YOUR_API_KEY"
keywords := []string{"OpenAI", "Anthropic", "Mistral AI"}
seen := map[string]bool{}
if b, err := os.ReadFile("seen_articles.json"); err == nil {
var links []string
json.Unmarshal(b, &links)
for _, l := range links { seen[l] = true }
}
var newCount int
for _, kw := range keywords {
for _, a := range fetchNews(apiKey, kw) {
link := a["link"].(string)
if !seen[link] {
seen[link] = true
fmt.Printf(" [%s] %s — %s\n", kw, a["title"], a["source"])
newCount++
}
}
}
links := make([]string, 0, len(seen))
for l := range seen { links = append(links, l) }
b, _ := json.Marshal(links)
os.WriteFile("seen_articles.json", b, 0644)
fmt.Printf("Found %d new article(s).\n", newCount)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
public static void main(String[] args) throws Exception {
var keywords = List.of("OpenAI", "Anthropic", "Mistral AI");
var seenPath = Path.of("seen_articles.json");
var seen = new HashSet();
if (Files.exists(seenPath)) {
var arr = new JSONArray(Files.readString(seenPath));
for (int i = 0; i < arr.length(); i++) seen.add(arr.getString(i));
}
int newCount = 0;
for (var kw : keywords) {
var q = URLEncoder.encode(kw, StandardCharsets.UTF_8);
var url = "https://api.autom.dev/v1/google/news?q=" + q + "&gl=us&hl=en";
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var results = new JSONObject(resp.body()).getJSONArray("organic_results");
for (int i = 0; i < results.length(); i++) {
var a = results.getJSONObject(i);
var link = a.getString("link");
if (!seen.contains(link)) {
seen.add(link);
System.out.printf(" [%s] %s — %s%n", kw, a.getString("title"), a.getString("source"));
newCount++;
}
}
}
Files.writeString(seenPath, new JSONArray(seen).toString());
System.out.println("Found " + newCount + " new article(s).");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var keywords = new[] { "OpenAI", "Anthropic", "Mistral AI" };
var seenFile = "seen_articles.json";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var seen = File.Exists(seenFile)
? JsonSerializer.Deserialize>(File.ReadAllText(seenFile))!
: new HashSet();
int newCount = 0;
foreach (var keyword in keywords)
{
var url = $"https://api.autom.dev/v1/google/news?q={Uri.EscapeDataString(keyword)}&gl=us&hl=en";
var body = await client.GetStringAsync(url);
var json = JsonDocument.Parse(body).RootElement;
foreach (var a in json.GetProperty("organic_results").EnumerateArray())
{
var link = a.GetProperty("link").GetString()!;
if (seen.Add(link))
{
Console.WriteLine($" [{keyword}] {a.GetProperty("title")} — {a.GetProperty("source")}");
newCount++;
}
}
}
File.WriteAllText(seenFile, JsonSerializer.Serialize(seen));
Console.WriteLine($"Found {newCount} new article(s).");
```
```rust
use reqwest::Client;
use serde_json::Value;
use std::{collections::HashSet, fs, path::Path};
async fn fetch_news(client: &Client, api_key: &str, query: &str) -> Vec {
let data = client
.get("https://api.autom.dev/v1/google/news")
.header("x-api-key", api_key)
.query(&[("query", query), ("gl", "us"), ("hl", "en")])
.send().await.unwrap().json::().await.unwrap();
data["organic_results"].as_array().cloned().unwrap_or_default()
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let keywords = ["OpenAI", "Anthropic", "Mistral AI"];
let seen_path = Path::new("seen_articles.json");
let mut seen: HashSet = if seen_path.exists() {
serde_json::from_str::>(&fs::read_to_string(seen_path).unwrap())
.unwrap().into_iter().collect()
} else { HashSet::new() };
let mut new_count = 0;
for keyword in &keywords {
for article in fetch_news(&client, api_key, keyword).await {
let link = article["link"].as_str().unwrap_or("").to_string();
if seen.insert(link) {
println!(" [{}] {} — {}", keyword, article["title"].as_str().unwrap_or(""), article["source"].as_str().unwrap_or(""));
new_count += 1;
}
}
}
let seen_vec: Vec<&String> = seen.iter().collect();
fs::write(seen_path, serde_json::to_string(&seen_vec).unwrap()).unwrap();
println!("Found {new_count} new article(s).");
Ok(())
}
```
Schedule this script with a cron job (e.g. every hour) or a task scheduler to receive continuous coverage monitoring. Combine multiple keywords in one run to minimize credit usage.
# Scrape Google Search Results (/playbooks/autom-google-search)
## Overview [#overview]
In this playbook you will build a script that queries Google Search and extracts structured organic results — positions, titles, URLs, and snippets — for any keyword. A typical use case is **rank tracking**: run this on a schedule to monitor where your pages appear for target keywords.
The endpoint returns up to 10 organic results per page and supports pagination, country (`gl`) and language (`hl`) targeting.
## Prerequisites [#prerequisites]
* An Autom API key — get one at [app.autom.dev](https://app.autom.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default in most PHP installs).
No extra dependencies — uses the `net/http` standard library (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Make your first search request [#make-your-first-search-request]
Call `GET /v1/google/search` with the `q` parameter and your API key in the `x-api-key` header.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.autom.dev/v1/google/search",
headers={"x-api-key": API_KEY},
params={"query": "best python web scraping libraries", "gl": "us", "hl": "en"},
)
data = response.json()
print(data)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({
q: "best python web scraping libraries",
gl: "us",
hl: "en",
});
const response = await fetch(
`https://api.autom.dev/v1/google/search?${params}`,
{ headers: { "x-api-key": API_KEY } },
);
const data = await response.json();
console.log(data);
```
```php
"best python web scraping libraries",
"gl" => "us",
"hl" => "en",
]);
$ch = curl_init("https://api.autom.dev/v1/google/search?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
apiKey := "YOUR_API_KEY"
params := url.Values{}
params.Set("query", "best python web scraping libraries")
params.Set("gl", "us")
params.Set("hl", "en")
req, _ := http.NewRequest("GET",
"https://api.autom.dev/v1/google/search?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
fmt.Println(data)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws Exception {
var apiKey = "YOUR_API_KEY";
var q = URLEncoder.encode("best python web scraping libraries", StandardCharsets.UTF_8);
var url = "https://api.autom.dev/v1/google/search?q=" + q + "&gl=us&hl=en";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("x-api-key", apiKey)
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
```
```csharp
using System.Net.Http;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var url = "https://api.autom.dev/v1/google/search?q=best+python+web+scraping+libraries&gl=us&hl=en";
var response = await client.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
```
```rust
use reqwest::header;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = reqwest::Client::new();
let response = client
.get("https://api.autom.dev/v1/google/search")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("query", "best python web scraping libraries"), ("gl", "us"), ("hl", "en")])
.send()
.await?
.json::()
.await?;
println!("{:#?}", response);
Ok(())
}
```
### Parse the organic results [#parse-the-organic-results]
The response contains an `organic_results` array. Each entry has `position`, `title`, `link`, `snippet`, `domain`, and `source`.
```python
for result in data["organic_results"]:
print(f"[{result['position']}] {result['title']}")
print(f" URL: {result['link']}")
print(f" {result.get('snippet', '')}")
print()
```
```typescript
for (const result of data.organic_results) {
console.log(`[${result.position}] ${result.title}`);
console.log(` URL: ${result.link}`);
console.log(` ${result.snippet ?? ""}`);
console.log();
}
```
```php
foreach ($data["organic_results"] as $result) {
echo "[{$result['position']}] {$result['title']}\n";
echo " URL: {$result['link']}\n";
echo " " . ($result['snippet'] ?? "") . "\n\n";
}
```
```go
results := data["organic_results"].([]any)
for _, r := range results {
item := r.(map[string]any)
fmt.Printf("[%.0f] %s\n", item["position"], item["title"])
fmt.Printf(" URL: %s\n", item["link"])
fmt.Printf(" %s\n\n", item["snippet"])
}
```
```java
import org.json.JSONArray;
import org.json.JSONObject;
// Add org.json:json to your build tool, or parse manually
var json = new JSONObject(response.body());
var results = json.getJSONArray("organic_results");
for (int i = 0; i < results.length(); i++) {
var r = results.getJSONObject(i);
System.out.printf("[%d] %s%n", r.getInt("position"), r.getString("title"));
System.out.printf(" URL: %s%n%n", r.getString("link"));
}
```
```csharp
using System.Text.Json;
var json = JsonDocument.Parse(body);
var results = json.RootElement.GetProperty("organic_results").EnumerateArray();
foreach (var result in results)
{
Console.WriteLine($"[{result.GetProperty("position")}] {result.GetProperty("title")}");
Console.WriteLine($" URL: {result.GetProperty("link")}");
Console.WriteLine();
}
```
```rust
if let Some(results) = response["organic_results"].as_array() {
for result in results {
println!(
"[{}] {}",
result["position"],
result["title"].as_str().unwrap_or("")
);
println!(" URL: {}", result["link"].as_str().unwrap_or(""));
println!(" {}", result["snippet"].as_str().unwrap_or(""));
println!();
}
}
```
### Handle pagination [#handle-pagination]
Use the `page` parameter to fetch subsequent pages. The response includes a `pagination` object with `has_next_page` and `next` page number.
```python
import requests
API_KEY = "YOUR_API_KEY"
QUERY = "best python web scraping libraries"
def fetch_all_results(query: str, max_pages: int = 3) -> list:
all_results = []
page = 1
while page <= max_pages:
response = requests.get(
"https://api.autom.dev/v1/google/search",
headers={"x-api-key": API_KEY},
params={"query": query, "gl": "us", "hl": "en", "page": page},
)
data = response.json()
all_results.extend(data.get("organic_results", []))
if not data.get("pagination", {}).get("has_next_page"):
break
page += 1
return all_results
results = fetch_all_results(QUERY)
for r in results:
print(f"[{r['position']}] {r['title']} — {r['link']}")
```
```typescript
const API_KEY = "YOUR_API_KEY";
async function fetchAllResults(query: string, maxPages = 3) {
const allResults: any[] = [];
let page = 1;
while (page <= maxPages) {
const params = new URLSearchParams({ q: query, gl: "us", hl: "en", page: String(page) });
const res = await fetch(`https://api.autom.dev/v1/google/search?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await res.json();
allResults.push(...(data.organic_results ?? []));
if (!data.pagination?.has_next_page) break;
page++;
}
return allResults;
}
const results = await fetchAllResults("best python web scraping libraries");
for (const r of results) {
console.log(`[${r.position}] ${r.title} — ${r.link}`);
}
```
```php
$query, "gl" => "us", "hl" => "en", "page" => $page]);
$ch = curl_init("https://api.autom.dev/v1/google/search?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$allResults = array_merge($allResults, $data["organic_results"] ?? []);
if (!($data["pagination"]["has_next_page"] ?? false)) break;
$page++;
}
return $allResults;
}
$results = fetchAllResults("YOUR_API_KEY", "best python web scraping libraries");
foreach ($results as $r) {
echo "[{$r['position']}] {$r['title']} — {$r['link']}\n";
}
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
)
func fetchAllResults(apiKey, query string, maxPages int) []map[string]any {
var all []map[string]any
for page := 1; page <= maxPages; page++ {
params := url.Values{"query": {query}, "gl": {"us"}, "hl": {"en"}, "page": {strconv.Itoa(page)}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/search?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var data map[string]any
json.Unmarshal(body, &data)
if items, ok := data["organic_results"].([]any); ok {
for _, item := range items {
all = append(all, item.(map[string]any))
}
}
pagination, _ := data["pagination"].(map[string]any)
if pagination["has_next_page"] != true {
break
}
}
return all
}
func main() {
results := fetchAllResults("YOUR_API_KEY", "best python web scraping libraries", 3)
for _, r := range results {
fmt.Printf("[%.0f] %s — %s\n", r["position"], r["title"], r["link"])
}
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
static List fetchAllResults(String query, int maxPages) throws Exception {
var all = new ArrayList();
var q = URLEncoder.encode(query, StandardCharsets.UTF_8);
for (int page = 1; page <= maxPages; page++) {
var url = "https://api.autom.dev/v1/google/search?q=" + q + "&gl=us&hl=en&page=" + page;
var request = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(request, HttpResponse.BodyHandlers.ofString());
var json = new JSONObject(resp.body());
var results = json.optJSONArray("organic_results");
if (results != null) {
for (int i = 0; i < results.length(); i++) all.add(results.getJSONObject(i));
}
if (!json.optJSONObject("pagination").optBoolean("has_next_page")) break;
}
return all;
}
public static void main(String[] args) throws Exception {
for (var r : fetchAllResults("best python web scraping libraries", 3)) {
System.out.printf("[%d] %s — %s%n", r.getInt("position"), r.getString("title"), r.getString("link"));
}
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var allResults = new List();
int page = 1, maxPages = 3;
while (page <= maxPages)
{
var url = $"https://api.autom.dev/v1/google/search?q=best+python+web+scraping+libraries&gl=us&hl=en&page={page}";
var response = await client.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
var json = JsonDocument.Parse(body).RootElement;
foreach (var result in json.GetProperty("organic_results").EnumerateArray())
allResults.Add(result);
var hasNext = json.GetProperty("pagination").GetProperty("has_next_page").GetBoolean();
if (!hasNext) break;
page++;
}
foreach (var r in allResults)
Console.WriteLine($"[{r.GetProperty("position")}] {r.GetProperty("title")} — {r.GetProperty("link")}");
```
```rust
use reqwest::Client;
use serde_json::Value;
async fn fetch_all_results(client: &Client, api_key: &str, query: &str, max_pages: u32) -> Vec {
let mut all_results = Vec::new();
let mut page = 1u32;
while page <= max_pages {
let resp = client
.get("https://api.autom.dev/v1/google/search")
.header("x-api-key", api_key)
.query(&[("query", query), ("gl", "us"), ("hl", "en"), ("page", &page.to_string())])
.send().await.unwrap()
.json::().await.unwrap();
if let Some(results) = resp["organic_results"].as_array() {
all_results.extend(results.clone());
}
if !resp["pagination"]["has_next_page"].as_bool().unwrap_or(false) { break; }
page += 1;
}
all_results
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let results = fetch_all_results(&client, "YOUR_API_KEY", "best python web scraping libraries", 3).await;
for r in &results {
println!("[{}] {} — {}", r["position"], r["title"].as_str().unwrap_or(""), r["link"].as_str().unwrap_or(""));
}
Ok(())
}
```
Use the `gl` parameter to target a specific country (e.g. `fr` for France, `de` for Germany) and `hl` for the language of results. This is essential for accurate local rank tracking.
# Analyze a Page with AI (/playbooks/capturekit-ai-analysis)
## Overview [#overview]
The CaptureKit AI analysis endpoint combines screenshot capture with LLM-powered analysis. Pass any URL and a custom `prompt` — the API returns a structured JSON answer grounded in what the page actually looks like. Use it for **competitor audits**, **UX reviews**, or **automated content QA**.
## Prerequisites [#prerequisites]
* A CaptureKit API key — get one at [app.capturekit.dev](https://app.capturekit.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Submit an analysis request [#submit-an-analysis-request]
Call `GET /v1/analyze` with the `url` and `prompt` parameters. The `prompt` shapes the AI's response.
```python
import requests
API_KEY = "YOUR_API_KEY"
PROMPT = "Summarize the main value proposition of this page in 2 sentences. Then list the top 3 CTAs visible above the fold."
response = requests.get(
"https://api.capturekit.dev/v1/analyze",
headers={"x-api-key": API_KEY},
params={"url": "https://stripe.com", "prompt": PROMPT},
)
data = response.json()
print(data)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const PROMPT = "Summarize the main value proposition of this page in 2 sentences. Then list the top 3 CTAs visible above the fold.";
const params = new URLSearchParams({ url: "https://stripe.com", prompt: PROMPT });
const response = await fetch(`https://api.capturekit.dev/v1/analyze?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
console.log(data);
```
```php
"https://stripe.com", "prompt" => $prompt]);
$ch = curl_init("https://api.capturekit.dev/v1/analyze?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{
"url": {"https://stripe.com"},
"prompt": {"Summarize the main value proposition in 2 sentences. List the top 3 CTAs above the fold."},
}
req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/analyze?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
fmt.Println(data)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
var client = HttpClient.newHttpClient();
var prompt = URLEncoder.encode("Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold.", StandardCharsets.UTF_8);
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.capturekit.dev/v1/analyze?url=https%3A%2F%2Fstripe.com&prompt=" + prompt))
.header("x-api-key", "YOUR_API_KEY").GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var prompt = Uri.EscapeDataString("Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold.");
var body = await client.GetStringAsync(
$"https://api.capturekit.dev/v1/analyze?url=https%3A%2F%2Fstripe.com&prompt={prompt}");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.capturekit.dev/v1/analyze")
.header("x-api-key", "YOUR_API_KEY")
.query(&[
("url", "https://stripe.com"),
("prompt", "Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold."),
])
.send().await?.json::().await?;
println!("{:#?}", data);
Ok(())
}
```
### Read the AI analysis [#read-the-ai-analysis]
The response includes `analysis` (the AI's answer to your prompt), `screenshot_url`, and metadata like `title` and `description`.
```python
print(f"Page : {data.get('title')}")
print(f"Preview : {data.get('screenshot_url')}")
print()
print("=== AI Analysis ===")
print(data.get("analysis", ""))
```
```typescript
console.log(`Page : ${data.title}`);
console.log(`Preview : ${data.screenshot_url}\n`);
console.log("=== AI Analysis ===");
console.log(data.analysis);
```
```php
echo "Page : {$data['title']}\n";
echo "Preview : {$data['screenshot_url']}\n\n";
echo "=== AI Analysis ===\n";
echo $data["analysis"] ?? "";
```
```go
fmt.Printf("Page : %v\nPreview : %v\n\n=== AI Analysis ===\n%v\n",
data["title"], data["screenshot_url"], data["analysis"])
```
```java
import org.json.*;
var d = new JSONObject(response.body());
System.out.printf("Page : %s%nPreview : %s%n%n=== AI Analysis ===%n%s%n",
d.getString("title"), d.getString("screenshot_url"), d.getString("analysis"));
```
```csharp
using System.Text.Json;
var d = JsonDocument.Parse(body).RootElement;
Console.WriteLine($"Page : {d.GetProperty("title")}");
Console.WriteLine($"Preview : {d.GetProperty("screenshot_url")}\n");
Console.WriteLine("=== AI Analysis ===");
Console.WriteLine(d.GetProperty("analysis"));
```
```rust
println!("Page : {}", data["title"].as_str().unwrap_or(""));
println!("Preview : {}\n", data["screenshot_url"].as_str().unwrap_or(""));
println!("=== AI Analysis ===");
println!("{}", data["analysis"].as_str().unwrap_or(""));
```
### Run a competitor audit across multiple pages [#run-a-competitor-audit-across-multiple-pages]
Analyze several competitor landing pages with a consistent prompt and save the results as a structured report.
````python
import json, time, requests
API_KEY = "YOUR_API_KEY"
PROMPT = """Analyze this landing page and return a JSON object with these keys:
- value_proposition (string)
- primary_cta (string)
- target_audience (string)
- pricing_visible (boolean)
- social_proof_types (array of strings: "testimonials", "logos", "stats", etc.)"""
COMPETITORS = [
{"name": "Stripe", "url": "https://stripe.com"},
{"name": "Paddle", "url": "https://paddle.com"},
{"name": "Lemonsqueezy", "url": "https://lemonsqueezy.com"},
]
report = []
for comp in COMPETITORS:
r = requests.get("https://api.capturekit.dev/v1/analyze",
headers={"x-api-key": API_KEY},
params={"url": comp["url"], "prompt": PROMPT})
data = r.json()
analysis_text = data.get("analysis", "{}")
try:
# AI may return the JSON wrapped in markdown fences
raw = analysis_text.strip().removeprefix("```json").removesuffix("```").strip()
analysis = json.loads(raw)
except json.JSONDecodeError:
analysis = {"raw": analysis_text}
report.append({"competitor": comp["name"], "url": comp["url"], **analysis})
print(f"✓ {comp['name']} analyzed")
time.sleep(2)
with open("competitor_audit.json", "w") as f:
json.dump(report, f, indent=2)
print("\nReport saved to competitor_audit.json")
````
````typescript
import { writeFileSync } from "fs";
const API_KEY = "YOUR_API_KEY";
const PROMPT = `Analyze this landing page and return a JSON object with these keys:
- value_proposition (string)
- primary_cta (string)
- target_audience (string)
- pricing_visible (boolean)
- social_proof_types (array of strings)`;
const competitors = [
{ name: "Stripe", url: "https://stripe.com" },
{ name: "Paddle", url: "https://paddle.com" },
{ name: "Lemonsqueezy", url: "https://lemonsqueezy.com" },
];
const report: any[] = [];
for (const comp of competitors) {
const params = new URLSearchParams({ url: comp.url, prompt: PROMPT });
const res = await fetch(`https://api.capturekit.dev/v1/analyze?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await res.json();
let analysis: any;
try {
const raw = (data.analysis ?? "{}").replace(/^```json\n?/, "").replace(/```$/, "").trim();
analysis = JSON.parse(raw);
} catch { analysis = { raw: data.analysis }; }
report.push({ competitor: comp.name, url: comp.url, ...analysis });
console.log(`✓ ${comp.name} analyzed`);
await new Promise(r => setTimeout(r, 2000));
}
writeFileSync("competitor_audit.json", JSON.stringify(report, null, 2));
console.log("\nReport saved to competitor_audit.json");
````
````php
"Stripe", "url" => "https://stripe.com"],
["name" => "Paddle", "url" => "https://paddle.com"],
["name" => "Lemonsqueezy", "url" => "https://lemonsqueezy.com"],
];
$report = [];
foreach ($competitors as $comp) {
$params = http_build_query(["url" => $comp["url"], "prompt" => $prompt]);
$ch = curl_init("https://api.capturekit.dev/v1/analyze?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$raw = preg_replace('/^```json\n?|```$/', '', trim($data["analysis"] ?? "{}"));
$analysis = json_decode($raw, true) ?? ["raw" => $data["analysis"]];
$report[] = array_merge(["competitor" => $comp["name"], "url" => $comp["url"]], $analysis);
echo "✓ {$comp['name']} analyzed\n";
sleep(2);
}
file_put_contents("competitor_audit.json", json_encode($report, JSON_PRETTY_PRINT));
echo "\nReport saved to competitor_audit.json\n";
````
````go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
const (
APIKey = "YOUR_API_KEY"
Prompt = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types."
)
type Competitor struct{ Name, URL string }
func analyze(comp Competitor) map[string]any {
params := url.Values{"url": {comp.URL}, "prompt": {Prompt}}
req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/analyze?"+params.Encode(), nil)
req.Header.Set("x-api-key", APIKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var data map[string]any
json.Unmarshal(body, &data)
raw := strings.TrimSpace(fmt.Sprint(data["analysis"]))
raw = strings.TrimPrefix(raw, "```json")
raw = strings.TrimSuffix(raw, "```")
var analysis map[string]any
if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &analysis); err != nil {
analysis = map[string]any{"raw": raw}
}
analysis["competitor"] = comp.Name
analysis["url"] = comp.URL
return analysis
}
func main() {
competitors := []Competitor{
{"Stripe", "https://stripe.com"},
{"Paddle", "https://paddle.com"},
{"Lemonsqueezy", "https://lemonsqueezy.com"},
}
var report []map[string]any
for _, c := range competitors {
report = append(report, analyze(c))
fmt.Printf("✓ %s analyzed\n", c.Name)
time.Sleep(2 * time.Second)
}
b, _ := json.MarshalIndent(report, "", " ")
os.WriteFile("competitor_audit.json", b, 0644)
fmt.Println("\nReport saved to competitor_audit.json")
}
````
````java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
static final String API_KEY = "YOUR_API_KEY";
static final String PROMPT = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var competitors = List.of(
Map.of("name","Stripe", "url","https://stripe.com"),
Map.of("name","Paddle", "url","https://paddle.com"),
Map.of("name","Lemonsqueezy", "url","https://lemonsqueezy.com"));
var report = new JSONArray();
for (var comp : competitors) {
var encodedUrl = URLEncoder.encode(comp.get("url"), StandardCharsets.UTF_8);
var encodedPrompt = URLEncoder.encode(PROMPT, StandardCharsets.UTF_8);
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.capturekit.dev/v1/analyze?url=" + encodedUrl + "&prompt=" + encodedPrompt))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var data = new JSONObject(resp.body());
var analysisText = data.getString("analysis")
.replaceAll("^```json\\n?","").replaceAll("```$","").strip();
JSONObject analysis;
try { analysis = new JSONObject(analysisText); }
catch (Exception e) { analysis = new JSONObject().put("raw", analysisText); }
analysis.put("competitor", comp.get("name")).put("url", comp.get("url"));
report.put(analysis);
System.out.println("✓ " + comp.get("name") + " analyzed");
Thread.sleep(2000);
}
Files.writeString(Path.of("competitor_audit.json"), report.toString(2));
System.out.println("\nReport saved to competitor_audit.json");
}
}
````
````csharp
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
const string API_KEY = "YOUR_API_KEY";
const string PROMPT = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", API_KEY);
var competitors = new[] {
(name: "Stripe", url: "https://stripe.com"),
(name: "Paddle", url: "https://paddle.com"),
(name: "Lemonsqueezy", url: "https://lemonsqueezy.com"),
};
var report = new List
````rust
use reqwest::Client;
use serde_json::{json, Value};
use std::{fs, time::Duration};
use tokio::time::sleep;
const API_KEY: &str = "YOUR_API_KEY";
const PROMPT: &str = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let competitors = [("Stripe","https://stripe.com"), ("Paddle","https://paddle.com"), ("Lemonsqueezy","https://lemonsqueezy.com")];
let mut report = Vec::new();
for (name, url) in &competitors {
let data = client.get("https://api.capturekit.dev/v1/analyze")
.header("x-api-key", API_KEY)
.query(&[("url", url), ("prompt", &PROMPT)])
.send().await?.json::().await?;
let analysis_text = data["analysis"].as_str().unwrap_or("{}");
let clean = analysis_text.trim().trim_start_matches("```json").trim_end_matches("```").trim();
let analysis: Value = serde_json::from_str(clean).unwrap_or(json!({ "raw": analysis_text }));
report.push(json!({ "competitor": name, "url": url, "analysis": analysis }));
println!("✓ {} analyzed", name);
sleep(Duration::from_secs(2)).await;
}
fs::write("competitor_audit.json", serde_json::to_string_pretty(&report).unwrap()).unwrap();
println!("\nReport saved to competitor_audit.json");
Ok(())
}
````
Instruct the AI to return structured JSON in your `prompt` — the model will format its output accordingly, making it easy to parse and store results in a database or spreadsheet without additional post-processing.
# Extract Content as Markdown (/playbooks/capturekit-content-extraction)
## Overview [#overview]
The CaptureKit content endpoint fetches a webpage, strips navigation, ads, and layout chrome, then returns the main body as **clean Markdown**. Use it for building RAG datasets, indexing documentation, or archiving articles.
## Prerequisites [#prerequisites]
* A CaptureKit API key — get one at [app.capturekit.dev](https://app.capturekit.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Fetch the page content [#fetch-the-page-content]
Call `GET /v1/content` with the `url` parameter.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.capturekit.dev/v1/content",
headers={"x-api-key": API_KEY},
params={"url": "https://stripe.com/docs/payments"},
)
data = response.json()
print(data)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ url: "https://stripe.com/docs/payments" });
const response = await fetch(`https://api.capturekit.dev/v1/content?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
console.log(data);
```
```php
"https://stripe.com/docs/payments"]);
$ch = curl_init("https://api.capturekit.dev/v1/content?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"url": {"https://stripe.com/docs/payments"}}
req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/content?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
fmt.Println(data)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
var client = HttpClient.newHttpClient();
var target = URLEncoder.encode("https://stripe.com/docs/payments", StandardCharsets.UTF_8);
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.capturekit.dev/v1/content?url=" + target))
.header("x-api-key", "YOUR_API_KEY").GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var target = Uri.EscapeDataString("https://stripe.com/docs/payments");
var body = await client.GetStringAsync($"https://api.capturekit.dev/v1/content?url={target}");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.capturekit.dev/v1/content")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("url", "https://stripe.com/docs/payments")])
.send().await?.json::().await?;
println!("{:#?}", data);
Ok(())
}
```
### Access the Markdown content [#access-the-markdown-content]
The response includes `markdown`, `title`, `description`, `author`, `published_at`, and `word_count`.
```python
print(f"Title : {data.get('title')}")
print(f"Author : {data.get('author', 'N/A')}")
print(f"Word count : {data.get('word_count')} words")
print()
print("--- Markdown preview ---")
markdown = data.get("markdown", "")
print(markdown[:1000])
```
```typescript
console.log(`Title : ${data.title}`);
console.log(`Author : ${data.author ?? "N/A"}`);
console.log(`Word count : ${data.word_count} words\n`);
console.log("--- Markdown preview ---");
console.log(data.markdown?.slice(0, 1000));
```
```php
echo "Title : {$data['title']}\n";
echo "Author : " . ($data["author"] ?? "N/A") . "\n";
echo "Word count : {$data['word_count']} words\n\n";
echo "--- Markdown preview ---\n";
echo substr($data["markdown"] ?? "", 0, 1000) . "\n";
```
```go
fmt.Printf("Title : %v\nAuthor : %v\nWord count : %v words\n\n",
data["title"], data["author"], data["word_count"])
markdown := data["markdown"].(string)
if len(markdown) > 1000 { markdown = markdown[:1000] }
fmt.Println("--- Markdown preview ---\n" + markdown)
```
```java
import org.json.*;
var d = new JSONObject(response.body());
var markdown = d.getString("markdown");
System.out.printf("Title : %s%nAuthor : %s%nWord count : %d words%n%n",
d.getString("title"), d.optString("author", "N/A"), d.getInt("word_count"));
System.out.println("--- Markdown preview ---");
System.out.println(markdown.substring(0, Math.min(1000, markdown.length())));
```
```csharp
using System.Text.Json;
var d = JsonDocument.Parse(body).RootElement;
var markdown = d.GetProperty("markdown").GetString() ?? "";
Console.WriteLine($"Title : {d.GetProperty("title")}");
Console.WriteLine($"Author : {(d.TryGetProperty("author", out var a) ? a : (object)"N/A")}");
Console.WriteLine($"Word count : {d.GetProperty("word_count")} words\n");
Console.WriteLine("--- Markdown preview ---");
Console.WriteLine(markdown[..Math.Min(1000, markdown.Length)]);
```
```rust
let markdown = data["markdown"].as_str().unwrap_or("");
println!("Title : {}", data["title"].as_str().unwrap_or(""));
println!("Author : {}", data["author"].as_str().unwrap_or("N/A"));
println!("Word count : {} words\n", data["word_count"]);
println!("--- Markdown preview ---");
println!("{}", &markdown[..1000.min(markdown.len())]);
```
### Crawl and archive a list of documentation pages [#crawl-and-archive-a-list-of-documentation-pages]
Fetch and save multiple pages as Markdown files for offline search or RAG ingestion.
```python
import os, time, requests
API_KEY = "YOUR_API_KEY"
os.makedirs("docs_archive", exist_ok=True)
PAGES = [
"https://stripe.com/docs/payments",
"https://stripe.com/docs/billing",
"https://stripe.com/docs/connect",
]
for page_url in PAGES:
r = requests.get("https://api.capturekit.dev/v1/content",
headers={"x-api-key": API_KEY}, params={"url": page_url})
data = r.json()
slug = page_url.rstrip("/").split("/")[-1]
path = f"docs_archive/{slug}.md"
with open(path, "w") as f:
f.write(f"# {data.get('title', slug)}\n\n")
f.write(data.get("markdown", ""))
print(f"Saved: {path} ({data.get('word_count', 0)} words)")
time.sleep(1)
print("Done!")
```
```typescript
import { mkdirSync, writeFileSync } from "fs";
const API_KEY = "YOUR_API_KEY";
mkdirSync("docs_archive", { recursive: true });
const PAGES = [
"https://stripe.com/docs/payments",
"https://stripe.com/docs/billing",
"https://stripe.com/docs/connect",
];
for (const pageUrl of PAGES) {
const params = new URLSearchParams({ url: pageUrl });
const res = await fetch(`https://api.capturekit.dev/v1/content?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await res.json();
const slug = pageUrl.replace(/\/$/, "").split("/").at(-1)!;
writeFileSync(`docs_archive/${slug}.md`, `# ${data.title ?? slug}\n\n${data.markdown ?? ""}`);
console.log(`Saved: docs_archive/${slug}.md (${data.word_count ?? 0} words)`);
await new Promise(r => setTimeout(r, 1000));
}
console.log("Done!");
```
```php
$pageUrl]);
$ch = curl_init("https://api.capturekit.dev/v1/content?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$slug = basename(rtrim($pageUrl, "/"));
$path = "docs_archive/{$slug}.md";
file_put_contents($path, "# {$data['title']}\n\n{$data['markdown']}");
echo "Saved: {$path} ({$data['word_count']} words)\n";
sleep(1);
}
echo "Done!\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
func main() {
os.MkdirAll("docs_archive", 0755)
apiKey := "YOUR_API_KEY"
pages := []string{
"https://stripe.com/docs/payments",
"https://stripe.com/docs/billing",
"https://stripe.com/docs/connect",
}
for _, pageURL := range pages {
params := url.Values{"url": {pageURL}}
req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/content?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var data map[string]any
json.Unmarshal(body, &data)
parts := strings.Split(strings.TrimRight(pageURL, "/"), "/")
slug := parts[len(parts)-1]
path := filepath.Join("docs_archive", slug+".md")
content := fmt.Sprintf("# %v\n\n%v", data["title"], data["markdown"])
os.WriteFile(path, []byte(content), 0644)
fmt.Printf("Saved: %s (%.0f words)\n", path, data["word_count"])
time.Sleep(time.Second)
}
fmt.Println("Done!")
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
import org.json.*;
public class Main {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var apiKey = "YOUR_API_KEY";
Files.createDirectories(Path.of("docs_archive"));
var pages = List.of(
"https://stripe.com/docs/payments",
"https://stripe.com/docs/billing",
"https://stripe.com/docs/connect");
for (var pageUrl : pages) {
var encoded = URLEncoder.encode(pageUrl, StandardCharsets.UTF_8);
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.capturekit.dev/v1/content?url=" + encoded))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var data = new JSONObject(resp.body());
var slug = pageUrl.replaceAll("/$","").replaceAll(".*/","");
var content = "# " + data.getString("title") + "\n\n" + data.getString("markdown");
Files.writeString(Path.of("docs_archive/" + slug + ".md"), content);
System.out.printf("Saved: docs_archive/%s.md (%d words)%n", slug, data.getInt("word_count"));
Thread.sleep(1000);
}
System.out.println("Done!");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
Directory.CreateDirectory("docs_archive");
var pages = new[]
{
"https://stripe.com/docs/payments",
"https://stripe.com/docs/billing",
"https://stripe.com/docs/connect",
};
foreach (var pageUrl in pages)
{
var encoded = Uri.EscapeDataString(pageUrl);
var body = await client.GetStringAsync($"https://api.capturekit.dev/v1/content?url={encoded}");
var data = JsonDocument.Parse(body).RootElement;
var slug = pageUrl.TrimEnd('/').Split('/').Last();
var content = $"# {data.GetProperty("title")}\n\n{data.GetProperty("markdown")}";
File.WriteAllText($"docs_archive/{slug}.md", content);
Console.WriteLine($"Saved: docs_archive/{slug}.md ({data.GetProperty("word_count")} words)");
await Task.Delay(1000);
}
Console.WriteLine("Done!");
```
```rust
use reqwest::Client;
use serde_json::Value;
use std::{fs, path::Path, time::Duration};
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let pages = ["https://stripe.com/docs/payments", "https://stripe.com/docs/billing", "https://stripe.com/docs/connect"];
fs::create_dir_all("docs_archive").unwrap();
for page_url in &pages {
let data = client.get("https://api.capturekit.dev/v1/content")
.header("x-api-key", api_key)
.query(&[("url", page_url)])
.send().await?.json::().await?;
let slug = page_url.trim_end_matches('/').split('/').last().unwrap_or("page");
let path = format!("docs_archive/{}.md", slug);
let content = format!("# {}\n\n{}", data["title"].as_str().unwrap_or(""), data["markdown"].as_str().unwrap_or(""));
fs::write(&path, content).unwrap();
println!("Saved: {} ({} words)", path, data["word_count"]);
sleep(Duration::from_secs(1)).await;
}
println!("Done!");
Ok(())
}
```
The extracted Markdown is ideal as context chunks for a RAG (Retrieval-Augmented Generation) pipeline. Chunk by headings and embed with your preferred vector store for semantic search over any website's documentation.
# Screenshot a Webpage (/playbooks/capturekit-screenshot)
## Overview [#overview]
This playbook shows how to take a screenshot of any public URL and save it to disk. A common use case is **visual regression testing** or generating OG image thumbnails for a link-preview service.
## Prerequisites [#prerequisites]
* A CaptureKit API key — get one at [app.capturekit.dev](https://app.capturekit.dev)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Request a screenshot [#request-a-screenshot]
Call `GET /v1/capture` with the `url` parameter. Additional options control the viewport, format, and full-page capture.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.capturekit.dev/v1/capture",
headers={"x-api-key": API_KEY},
params={
"url": "https://stripe.com",
"format": "png",
"full_page": "true",
"width": "1440",
"height": "900",
},
)
data = response.json()
print(data)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({
url: "https://stripe.com",
format: "png",
full_page: "true",
width: "1440",
height: "900",
});
const response = await fetch(`https://api.capturekit.dev/v1/capture?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
console.log(data);
```
```php
"https://stripe.com",
"format" => "png",
"full_page" => "true",
"width" => "1440",
"height" => "900",
]);
$ch = curl_init("https://api.capturekit.dev/v1/capture?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{
"url": {"https://stripe.com"}, "format": {"png"},
"full_page": {"true"}, "width": {"1440"}, "height": {"900"},
}
req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/capture?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
fmt.Println(data)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
var apiKey = "YOUR_API_KEY";
var target = URLEncoder.encode("https://stripe.com", StandardCharsets.UTF_8);
var url = "https://api.capturekit.dev/v1/capture?url=" + target + "&format=png&full_page=true&width=1440&height=900";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var target = Uri.EscapeDataString("https://stripe.com");
var body = await client.GetStringAsync(
$"https://api.capturekit.dev/v1/capture?url={target}&format=png&full_page=true&width=1440&height=900");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.capturekit.dev/v1/capture")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("url", "https://stripe.com"), ("format", "png"), ("full_page", "true"), ("width", "1440"), ("height", "900")])
.send().await?.json::().await?;
println!("{:#?}", data);
Ok(())
}
```
### Get the image URL from the response [#get-the-image-url-from-the-response]
The API returns a `screenshot_url` with the hosted image — or a base64-encoded `image` field if you set `response_type=base64`.
```python
screenshot_url = data.get("screenshot_url")
print(f"Screenshot ready: {screenshot_url}")
```
```typescript
const { screenshot_url } = data;
console.log(`Screenshot ready: ${screenshot_url}`);
```
```php
$screenshotUrl = $data["screenshot_url"] ?? "";
echo "Screenshot ready: {$screenshotUrl}\n";
```
```go
screenshotURL := data["screenshot_url"].(string)
fmt.Println("Screenshot ready:", screenshotURL)
```
```java
import org.json.*;
var screenshotUrl = new JSONObject(response.body()).getString("screenshot_url");
System.out.println("Screenshot ready: " + screenshotUrl);
```
```csharp
using System.Text.Json;
var screenshotUrl = JsonDocument.Parse(body).RootElement.GetProperty("screenshot_url").GetString();
Console.WriteLine($"Screenshot ready: {screenshotUrl}");
```
```rust
let screenshot_url = data["screenshot_url"].as_str().unwrap_or("");
println!("Screenshot ready: {}", screenshot_url);
```
### Download and save the image [#download-and-save-the-image]
Fetch the image bytes from the URL and write them to disk.
```python
filename = "screenshot.png"
with requests.get(screenshot_url, stream=True) as r:
r.raise_for_status()
with open(filename, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Saved to {filename}")
```
```typescript
import { writeFileSync } from "fs";
const imgResponse = await fetch(screenshotUrl);
const buffer = Buffer.from(await imgResponse.arrayBuffer());
writeFileSync("screenshot.png", buffer);
console.log("Saved to screenshot.png");
```
```php
$fp = fopen("screenshot.png", "wb");
$ch = curl_init($screenshotUrl);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
echo "Saved to screenshot.png\n";
```
```go
import "os"
resp, _ := http.Get(screenshotURL)
defer resp.Body.Close()
file, _ := os.Create("screenshot.png")
defer file.Close()
io.Copy(file, resp.Body)
fmt.Println("Saved to screenshot.png")
```
```java
import java.net.URL;
import java.nio.file.*;
Files.copy(new URL(screenshotUrl).openStream(), Path.of("screenshot.png"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Saved to screenshot.png");
```
```csharp
var imageBytes = await new HttpClient().GetByteArrayAsync(screenshotUrl);
File.WriteAllBytes("screenshot.png", imageBytes);
Console.WriteLine("Saved to screenshot.png");
```
```rust
use std::{fs::File, io::Write};
let bytes = reqwest::get(screenshot_url).await?.bytes().await?;
let mut file = File::create("screenshot.png").unwrap();
file.write_all(&bytes).unwrap();
println!("Saved to screenshot.png");
```
Use `full_page=false` to capture only the above-the-fold viewport — ideal for social media preview thumbnails where a fixed-height 630×1200 px crop is required.
# Extract Audio from a Video (/playbooks/huntapi-audio-download)
## Overview [#overview]
By passing `quality: "audio"` to the HuntAPI downloader, you get an MP3 extract instead of the full video. This playbook builds a complete audio extraction pipeline including job submission, polling, and saving.
## Prerequisites [#prerequisites]
* A HuntAPI key — get one at [app.huntapi.com](https://app.huntapi.com)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Submit an audio extraction job [#submit-an-audio-extraction-job]
Use the same `/v1/video/download` endpoint with `quality=audio`.
```python
import requests
API_KEY = "YOUR_API_KEY"
VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
response = requests.get(
"https://api.huntapi.com/v1/video/download",
headers={"x-api-key": API_KEY},
params={"url": VIDEO_URL, "quality": "audio"},
)
data = response.json()
job_id = data["job_id"]
print(f"Audio job submitted: {job_id}")
```
```typescript
const API_KEY = "YOUR_API_KEY";
const VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
const params = new URLSearchParams({ url: VIDEO_URL, quality: "audio" });
const response = await fetch(`https://api.huntapi.com/v1/video/download?${params}`, {
headers: { "x-api-key": API_KEY },
});
const { job_id } = await response.json();
console.log(`Audio job submitted: ${job_id}`);
```
```php
$videoUrl, "quality" => "audio"]);
$ch = curl_init("https://api.huntapi.com/v1/video/download?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$jobId = $data["job_id"];
echo "Audio job submitted: {$jobId}\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
const (
APIKey = "YOUR_API_KEY"
VideoURL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
)
func get(apiKey, rawURL string) map[string]any {
req, _ := http.NewRequest("GET", rawURL, nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var m map[string]any
json.Unmarshal(body, &m)
return m
}
func main() {
params := url.Values{"url": {VideoURL}, "quality": {"audio"}}
data := get(APIKey, "https://api.huntapi.com/v1/video/download?"+params.Encode())
jobID := data["job_id"].(string)
fmt.Println("Audio job submitted:", jobID)
// polling in next step...
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;
var apiKey = "YOUR_API_KEY";
var videoUrl = URLEncoder.encode("https://www.youtube.com/watch?v=dQw4w9WgXcQ", StandardCharsets.UTF_8);
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.huntapi.com/v1/video/download?url=" + videoUrl + "&quality=audio"))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var jobId = new JSONObject(resp.body()).getString("job_id");
System.out.println("Audio job submitted: " + jobId);
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var videoUrl = Uri.EscapeDataString("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var body = await client.GetStringAsync($"https://api.huntapi.com/v1/video/download?url={videoUrl}&quality=audio");
var jobId = JsonDocument.Parse(body).RootElement.GetProperty("job_id").GetString()!;
Console.WriteLine($"Audio job submitted: {jobId}");
```
```rust
use reqwest::Client;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let video_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
let data = client.get("https://api.huntapi.com/v1/video/download")
.header("x-api-key", api_key)
.query(&[("url", video_url), ("quality", "audio")])
.send().await?.json::().await?;
let job_id = data["job_id"].as_str().unwrap();
println!("Audio job submitted: {}", job_id);
Ok(())
}
```
### Poll for completion [#poll-for-completion]
Check the job status every 5 seconds until `status == "done"`.
```python
import time
def wait_for_job(job_id: str) -> dict:
for _ in range(60):
r = requests.get(f"https://api.huntapi.com/v1/job/{job_id}",
headers={"x-api-key": API_KEY})
result = r.json()
status = result["status"]
print(f" [{status}]")
if status == "done":
return result
if status == "error":
raise RuntimeError(result.get("error", "Unknown error"))
time.sleep(5)
raise TimeoutError("Job timed out")
result = wait_for_job(job_id)
download_url = result["download_url"]
```
```typescript
async function waitForJob(jobId: string) {
for (let i = 0; i < 60; i++) {
const res = await fetch(`https://api.huntapi.com/v1/job/${jobId}`, {
headers: { "x-api-key": API_KEY },
});
const result = await res.json();
console.log(` [${result.status}]`);
if (result.status === "done") return result;
if (result.status === "error") throw new Error(result.error ?? "Unknown error");
await new Promise(r => setTimeout(r, 5000));
}
throw new Error("Timeout");
}
const result = await waitForJob(job_id);
const downloadUrl = result.download_url;
```
```php
function waitForJob(string $apiKey, string $jobId): array {
for ($i = 0; $i < 60; $i++) {
$ch = curl_init("https://api.huntapi.com/v1/job/{$jobId}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
$status = $result["status"] ?? "";
echo " [{$status}]\n";
if ($status === "done") return $result;
if ($status === "error") throw new RuntimeException($result["error"] ?? "Unknown");
sleep(5);
}
throw new RuntimeException("Timeout");
}
$result = waitForJob($apiKey, $jobId);
$downloadUrl = $result["download_url"];
```
```go
func waitForJob(apiKey, jobID string) string {
for i := 0; i < 60; i++ {
data := get(apiKey, "https://api.huntapi.com/v1/job/"+jobID)
status := data["status"].(string)
fmt.Printf(" [%s]\n", status)
if status == "done" { return data["download_url"].(string) }
if status == "error" { panic("Job failed: " + fmt.Sprint(data["error"])) }
time.Sleep(5 * time.Second)
}
panic("Timeout")
}
downloadURL := waitForJob(APIKey, jobID)
```
```java
static String waitForJob(HttpClient client, String apiKey, String jobId) throws Exception {
for (int i = 0; i < 60; i++) {
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.huntapi.com/v1/job/" + jobId))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var result = new JSONObject(resp.body());
var status = result.getString("status");
System.out.println(" [" + status + "]");
if ("done".equals(status)) return result.getString("download_url");
if ("error".equals(status)) throw new RuntimeException(result.optString("error"));
Thread.sleep(5000);
}
throw new RuntimeException("Timeout");
}
var downloadUrl = waitForJob(client, apiKey, jobId);
```
```csharp
async Task WaitForJob(HttpClient client, string jobId)
{
for (int i = 0; i < 60; i++)
{
var body = await client.GetStringAsync($"https://api.huntapi.com/v1/job/{jobId}");
var result = JsonDocument.Parse(body).RootElement;
var status = result.GetProperty("status").GetString();
Console.WriteLine($" [{status}]");
if (status == "done") return result.GetProperty("download_url").GetString()!;
if (status == "error") throw new Exception(result.GetProperty("error").GetString());
await Task.Delay(5000);
}
throw new TimeoutException();
}
var downloadUrl = await WaitForJob(client, jobId);
```
```rust
use tokio::time::{sleep, Duration};
async fn wait_for_job(client: &Client, api_key: &str, job_id: &str) -> String {
for _ in 0..60 {
let result = client.get(format!("https://api.huntapi.com/v1/job/{}", job_id))
.header("x-api-key", api_key)
.send().await.unwrap().json::().await.unwrap();
let status = result["status"].as_str().unwrap_or("");
println!(" [{}]", status);
if status == "done" { return result["download_url"].as_str().unwrap().to_string(); }
if status == "error" { panic!("Job failed: {}", result["error"]); }
sleep(Duration::from_secs(5)).await;
}
panic!("Timeout")
}
```
### Save the audio file [#save-the-audio-file]
Download the MP3 and save it to disk.
```python
output_file = "audio.mp3"
with requests.get(download_url, stream=True) as r:
r.raise_for_status()
with open(output_file, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Audio saved to {output_file} ({os.path.getsize(output_file) // 1024} KB)")
```
```typescript
import { createWriteStream } from "fs";
import { Readable } from "stream";
const fileRes = await fetch(downloadUrl);
const writer = createWriteStream("audio.mp3");
Readable.fromWeb(fileRes.body as any).pipe(writer);
await new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); });
console.log("Audio saved to audio.mp3");
```
```php
$fp = fopen("audio.mp3", "wb");
$ch = curl_init($downloadUrl);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
echo "Audio saved to audio.mp3\n";
```
```go
resp, _ := http.Get(downloadURL)
defer resp.Body.Close()
file, _ := os.Create("audio.mp3")
defer file.Close()
io.Copy(file, resp.Body)
fmt.Println("Audio saved to audio.mp3")
```
```java
import java.net.URL;
import java.nio.file.*;
Files.copy(new URL(downloadUrl).openStream(), Path.of("audio.mp3"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Audio saved to audio.mp3");
```
```csharp
using var audioStream = await new HttpClient().GetStreamAsync(downloadUrl);
using var file = File.Create("audio.mp3");
await audioStream.CopyToAsync(file);
Console.WriteLine("Audio saved to audio.mp3");
```
```rust
use std::{fs::File, io::Write};
let bytes = reqwest::get(download_url).await?.bytes().await?;
let mut file = File::create("audio.mp3").unwrap();
file.write_all(&bytes).unwrap();
println!("Audio saved to audio.mp3");
```
The extracted audio is delivered as an MP3. You can pipe it directly to a transcription service (e.g. OpenAI Whisper or Deepgram) for automatic subtitling or search indexing.
# Download Your First Video (/playbooks/huntapi-video-download)
## Overview [#overview]
HuntAPI uses an **asynchronous job model**: you submit a URL, receive a `job_id`, then poll until the video is ready. This playbook walks through all three steps and downloads the finished file to disk.
## Prerequisites [#prerequisites]
* A HuntAPI key — get one at [app.huntapi.com](https://app.huntapi.com)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Submit the download job [#submit-the-download-job]
Call `GET /v1/video/download` with the `url` parameter. The response immediately returns a `job_id`.
```python
import requests
API_KEY = "YOUR_API_KEY"
VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
response = requests.get(
"https://api.huntapi.com/v1/video/download",
headers={"x-api-key": API_KEY},
params={"url": VIDEO_URL, "quality": "best"},
)
data = response.json()
job_id = data["job_id"]
print(f"Job submitted: {job_id}")
```
```typescript
const API_KEY = "YOUR_API_KEY";
const VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
const params = new URLSearchParams({ url: VIDEO_URL, quality: "best" });
const response = await fetch(`https://api.huntapi.com/v1/video/download?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();
const jobId = data.job_id;
console.log(`Job submitted: ${jobId}`);
```
```php
$videoUrl, "quality" => "best"]);
$ch = curl_init("https://api.huntapi.com/v1/video/download?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$jobId = $data["job_id"];
echo "Job submitted: {$jobId}\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
const APIKey = "YOUR_API_KEY"
const VideoURL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
func main() {
params := url.Values{"url": {VideoURL}, "quality": {"best"}}
req, _ := http.NewRequest("GET", "https://api.huntapi.com/v1/video/download?"+params.Encode(), nil)
req.Header.Set("x-api-key", APIKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
jobID := data["job_id"].(string)
fmt.Println("Job submitted:", jobID)
// continue in next step...
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;
var apiKey = "YOUR_API_KEY";
var videoUrl = URLEncoder.encode("https://www.youtube.com/watch?v=dQw4w9WgXcQ", StandardCharsets.UTF_8);
var url = "https://api.huntapi.com/v1/video/download?url=" + videoUrl + "&quality=best";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
var jobId = new JSONObject(response.body()).getString("job_id");
System.out.println("Job submitted: " + jobId);
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var videoUrl = Uri.EscapeDataString("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var body = await client.GetStringAsync($"https://api.huntapi.com/v1/video/download?url={videoUrl}&quality=best");
var jobId = JsonDocument.Parse(body).RootElement.GetProperty("job_id").GetString()!;
Console.WriteLine($"Job submitted: {jobId}");
```
```rust
use reqwest::Client;
use serde_json::Value;
let client = Client::new();
let api_key = "YOUR_API_KEY";
let video_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
let data = client.get("https://api.huntapi.com/v1/video/download")
.header("x-api-key", api_key)
.query(&[("url", video_url), ("quality", "best")])
.send().await?.json::().await?;
let job_id = data["job_id"].as_str().unwrap();
println!("Job submitted: {}", job_id);
```
### Poll until the video is ready [#poll-until-the-video-is-ready]
Check `GET /v1/job/{job_id}` every few seconds. When `status` becomes `"done"`, the `download_url` field contains the file URL.
```python
import time
def wait_for_job(job_id: str, poll_interval: int = 5, timeout: int = 300) -> dict:
start = time.time()
while time.time() - start < timeout:
r = requests.get(f"https://api.huntapi.com/v1/job/{job_id}",
headers={"x-api-key": API_KEY})
result = r.json()
status = result.get("status")
print(f" Status: {status}")
if status == "done":
return result
if status == "error":
raise RuntimeError(f"Job failed: {result.get('error')}")
time.sleep(poll_interval)
raise TimeoutError("Job did not complete within the timeout period.")
result = wait_for_job(job_id)
download_url = result["download_url"]
print(f"Ready! Download URL: {download_url}")
```
```typescript
async function waitForJob(jobId: string, pollMs = 5000, timeoutMs = 300_000) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`https://api.huntapi.com/v1/job/${jobId}`, {
headers: { "x-api-key": API_KEY },
});
const result = await res.json();
console.log(` Status: ${result.status}`);
if (result.status === "done") return result;
if (result.status === "error") throw new Error(`Job failed: ${result.error}`);
await new Promise(r => setTimeout(r, pollMs));
}
throw new Error("Timeout");
}
const result = await waitForJob(jobId);
const downloadUrl = result.download_url;
console.log(`Ready! Download URL: ${downloadUrl}`);
```
```php
function waitForJob(string $apiKey, string $jobId, int $pollSec = 5, int $timeoutSec = 300): array {
$start = time();
while (time() - $start < $timeoutSec) {
$ch = curl_init("https://api.huntapi.com/v1/job/{$jobId}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
$status = $result["status"] ?? "";
echo " Status: {$status}\n";
if ($status === "done") return $result;
if ($status === "error") throw new RuntimeException("Job failed: " . ($result["error"] ?? ""));
sleep($pollSec);
}
throw new RuntimeException("Timeout");
}
$result = waitForJob($apiKey, $jobId);
$downloadUrl = $result["download_url"];
echo "Ready! Download URL: {$downloadUrl}\n";
```
```go
import "time"
func waitForJob(apiKey, jobID string) (map[string]any, error) {
deadline := time.Now().Add(5 * time.Minute)
for time.Now().Before(deadline) {
req, _ := http.NewRequest("GET", "https://api.huntapi.com/v1/job/"+jobID, nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var result map[string]any
json.Unmarshal(body, &result)
status := result["status"].(string)
fmt.Println(" Status:", status)
if status == "done" { return result, nil }
if status == "error" { return nil, fmt.Errorf("job failed: %v", result["error"]) }
time.Sleep(5 * time.Second)
}
return nil, fmt.Errorf("timeout")
}
result, err := waitForJob(APIKey, jobID)
if err != nil { panic(err) }
downloadURL := result["download_url"].(string)
fmt.Println("Ready! Download URL:", downloadURL)
```
```java
import java.time.*;
static JSONObject waitForJob(HttpClient client, String apiKey, String jobId) throws Exception {
var deadline = Instant.now().plusSeconds(300);
while (Instant.now().isBefore(deadline)) {
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.huntapi.com/v1/job/" + jobId))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var result = new JSONObject(resp.body());
var status = result.getString("status");
System.out.println(" Status: " + status);
if ("done".equals(status)) return result;
if ("error".equals(status)) throw new RuntimeException("Job failed: " + result.optString("error"));
Thread.sleep(5000);
}
throw new RuntimeException("Timeout");
}
var result = waitForJob(client, apiKey, jobId);
var downloadUrl = result.getString("download_url");
System.out.println("Ready! Download URL: " + downloadUrl);
```
```csharp
async Task WaitForJob(HttpClient client, string jobId)
{
var deadline = DateTime.UtcNow.AddMinutes(5);
while (DateTime.UtcNow < deadline)
{
var body = await client.GetStringAsync($"https://api.huntapi.com/v1/job/{jobId}");
var result = JsonDocument.Parse(body).RootElement;
var status = result.GetProperty("status").GetString();
Console.WriteLine($" Status: {status}");
if (status == "done") return result;
if (status == "error") throw new Exception($"Job failed: {result.GetProperty("error")}");
await Task.Delay(5000);
}
throw new TimeoutException("Job did not complete in time.");
}
var result = await WaitForJob(client, jobId);
var downloadUrl = result.GetProperty("download_url").GetString()!;
Console.WriteLine($"Ready! Download URL: {downloadUrl}");
```
```rust
use tokio::time::{sleep, Duration};
use std::time::Instant;
async fn wait_for_job(client: &Client, api_key: &str, job_id: &str) -> Value {
let deadline = Instant::now() + Duration::from_secs(300);
loop {
assert!(Instant::now() < deadline, "Timeout");
let result = client.get(format!("https://api.huntapi.com/v1/job/{}", job_id))
.header("x-api-key", api_key)
.send().await.unwrap().json::().await.unwrap();
let status = result["status"].as_str().unwrap_or("");
println!(" Status: {}", status);
if status == "done" { return result; }
if status == "error" { panic!("Job failed: {}", result["error"]); }
sleep(Duration::from_secs(5)).await;
}
}
let result = wait_for_job(&client, api_key, job_id).await;
let download_url = result["download_url"].as_str().unwrap();
println!("Ready! Download URL: {}", download_url);
```
### Download the video file to disk [#download-the-video-file-to-disk]
Stream the file from the `download_url` and save it locally.
```python
filename = "video.mp4"
with requests.get(download_url, stream=True) as r:
r.raise_for_status()
with open(filename, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Saved to {filename}")
```
```typescript
import { createWriteStream } from "fs";
import { Readable } from "stream";
const fileResponse = await fetch(downloadUrl);
const writer = createWriteStream("video.mp4");
Readable.fromWeb(fileResponse.body as any).pipe(writer);
await new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); });
console.log("Saved to video.mp4");
```
```php
$fp = fopen("video.mp4", "wb");
$ch = curl_init($downloadUrl);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
echo "Saved to video.mp4\n";
```
```go
import "os"
resp, _ := http.Get(downloadURL)
defer resp.Body.Close()
file, _ := os.Create("video.mp4")
defer file.Close()
io.Copy(file, resp.Body)
fmt.Println("Saved to video.mp4")
```
```java
import java.nio.file.*;
import java.net.URL;
var in = new URL(downloadUrl).openStream();
Files.copy(in, Path.of("video.mp4"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Saved to video.mp4");
```
```csharp
using var fileStream = File.Create("video.mp4");
using var download = await new HttpClient().GetStreamAsync(downloadUrl);
await download.CopyToAsync(fileStream);
Console.WriteLine("Saved to video.mp4");
```
```rust
use std::io::Write;
use std::fs::File;
let bytes = reqwest::get(download_url).await?.bytes().await?;
let mut file = File::create("video.mp4").unwrap();
file.write_all(&bytes).unwrap();
println!("Saved to video.mp4");
```
You can pass `quality: "best"`, `"1080p"`, `"720p"`, or `"audio"` in the initial request to control the output format before the job is submitted.
# Batch Downloads with Webhooks (/playbooks/huntapi-webhook-batch)
## Overview [#overview]
Instead of polling, you can pass a `webhook_url` when submitting a job. HuntAPI will POST the result to your URL the moment the download is ready. This playbook shows how to:
1. Submit a batch of jobs with a webhook URL
2. Receive and verify the webhook payload
## Prerequisites [#prerequisites]
* A HuntAPI key — get one at [app.huntapi.com](https://app.huntapi.com)
* A publicly reachable HTTP endpoint (use [ngrok](https://ngrok.com) for local testing)
* Install dependencies for your language:
```bash
pip install requests flask
```
```bash
npm install express @types/express
```
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `com.sun.net.httpserver` (built-in, Java 6+).
No extra dependencies — uses ASP.NET minimal API (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
axum = "0.7"
```
## Steps [#steps]
### Submit a batch of jobs with a webhook URL [#submit-a-batch-of-jobs-with-a-webhook-url]
Pass your publicly accessible endpoint as `webhook_url`. Each job is independent; HuntAPI will call the webhook when it finishes.
```python
import requests
API_KEY = "YOUR_API_KEY"
WEBHOOK_URL = "https://yourserver.example.com/webhooks/huntapi"
URLS = [
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
]
job_ids = []
for video_url in URLS:
r = requests.get(
"https://api.huntapi.com/v1/video/download",
headers={"x-api-key": API_KEY},
params={"url": video_url, "quality": "best", "webhook_url": WEBHOOK_URL},
)
job_id = r.json()["job_id"]
job_ids.append(job_id)
print(f"Submitted: {job_id}")
print(f"\n{len(job_ids)} jobs submitted. Waiting for webhooks...")
```
```typescript
const API_KEY = "YOUR_API_KEY";
const WEBHOOK_URL = "https://yourserver.example.com/webhooks/huntapi";
const URLS = [
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
];
const jobIds: string[] = [];
for (const videoUrl of URLS) {
const params = new URLSearchParams({ url: videoUrl, quality: "best", webhook_url: WEBHOOK_URL });
const res = await fetch(`https://api.huntapi.com/v1/video/download?${params}`, {
headers: { "x-api-key": API_KEY },
});
const { job_id } = await res.json();
jobIds.push(job_id);
console.log(`Submitted: ${job_id}`);
}
console.log(`\n${jobIds.length} jobs submitted. Waiting for webhooks...`);
```
```php
$videoUrl, "quality" => "best", "webhook_url" => $webhookUrl]);
$ch = curl_init("https://api.huntapi.com/v1/video/download?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
$jobIds[] = $data["job_id"];
echo "Submitted: {$data['job_id']}\n";
}
echo count($jobIds) . " jobs submitted. Waiting for webhooks...\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func submitJob(apiKey, videoURL, webhookURL string) string {
params := url.Values{"url": {videoURL}, "quality": {"best"}, "webhook_url": {webhookURL}}
req, _ := http.NewRequest("GET", "https://api.huntapi.com/v1/video/download?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
return data["job_id"].(string)
}
func main() {
apiKey := "YOUR_API_KEY"
webhookURL := "https://yourserver.example.com/webhooks/huntapi"
urls := []string{
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
}
for _, u := range urls {
jobID := submitJob(apiKey, u, webhookURL)
fmt.Println("Submitted:", jobID)
}
fmt.Printf("%d jobs submitted. Waiting for webhooks...\n", len(urls))
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;
var client = HttpClient.newHttpClient();
var apiKey = "YOUR_API_KEY";
var webhookUrl = "https://yourserver.example.com/webhooks/huntapi";
var urls = new String[]{
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
};
for (var videoUrl : urls) {
var encoded = URLEncoder.encode(videoUrl, StandardCharsets.UTF_8);
var wh = URLEncoder.encode(webhookUrl, StandardCharsets.UTF_8);
var url = "https://api.huntapi.com/v1/video/download?url=" + encoded + "&quality=best&webhook_url=" + wh;
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var jobId = new JSONObject(resp.body()).getString("job_id");
System.out.println("Submitted: " + jobId);
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var webhookUrl = Uri.EscapeDataString("https://yourserver.example.com/webhooks/huntapi");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var urls = new[]
{
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
};
foreach (var videoUrl in urls)
{
var encoded = Uri.EscapeDataString(videoUrl);
var body = await client.GetStringAsync(
$"https://api.huntapi.com/v1/video/download?url={encoded}&quality=best&webhook_url={webhookUrl}");
var jobId = JsonDocument.Parse(body).RootElement.GetProperty("job_id").GetString();
Console.WriteLine($"Submitted: {jobId}");
}
```
```rust
use reqwest::Client;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let webhook_url = "https://yourserver.example.com/webhooks/huntapi";
let urls = [
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2",
"https://www.youtube.com/watch?v=VIDEO_ID_3",
];
for video_url in &urls {
let data = client.get("https://api.huntapi.com/v1/video/download")
.header("x-api-key", api_key)
.query(&[("url", video_url), ("quality", &"best"), ("webhook_url", &webhook_url)])
.send().await?.json::().await?;
println!("Submitted: {}", data["job_id"].as_str().unwrap_or(""));
}
Ok(())
}
```
### Build a webhook receiver [#build-a-webhook-receiver]
HuntAPI will POST a JSON body to your endpoint with `job_id`, `status`, and `download_url` when the job completes.
```python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/webhooks/huntapi", methods=["POST"])
def huntapi_webhook():
payload = request.get_json()
job_id = payload.get("job_id")
status = payload.get("status")
download_url = payload.get("download_url")
print(f"Webhook received — Job: {job_id}, Status: {status}")
if status == "done" and download_url:
# Trigger your download or further processing here
print(f" Download URL: {download_url}")
return jsonify({"received": True}), 200
if __name__ == "__main__":
app.run(port=3000)
```
```typescript
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/huntapi", (req, res) => {
const { job_id, status, download_url } = req.body;
console.log(`Webhook received — Job: ${job_id}, Status: ${status}`);
if (status === "done" && download_url) {
// Trigger your download or further processing here
console.log(` Download URL: ${download_url}`);
}
res.json({ received: true });
});
app.listen(3000, () => console.log("Webhook listener on :3000"));
```
```php
true]);
```
```go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/webhooks/huntapi", func(w http.ResponseWriter, r *http.Request) {
var payload map[string]any
json.NewDecoder(r.Body).Decode(&payload)
jobID := payload["job_id"]
status := payload["status"]
downloadURL := payload["download_url"]
fmt.Printf("Webhook received — Job: %v, Status: %v\n", jobID, status)
if status == "done" && downloadURL != nil {
fmt.Println(" Download URL:", downloadURL)
// Trigger download or further processing here
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"received":true}`))
})
fmt.Println("Webhook listener on :3000")
http.ListenAndServe(":3000", nil)
}
```
```java
import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import org.json.*;
public class WebhookServer {
public static void main(String[] args) throws Exception {
var server = HttpServer.create(new InetSocketAddress(3000), 0);
server.createContext("/webhooks/huntapi", exchange -> {
var body = exchange.getRequestBody().readAllBytes();
var payload = new JSONObject(new String(body));
var jobId = payload.optString("job_id");
var status = payload.optString("status");
var downloadUrl = payload.optString("download_url");
System.out.printf("Webhook received — Job: %s, Status: %s%n", jobId, status);
if ("done".equals(status) && !downloadUrl.isEmpty()) {
System.out.println(" Download URL: " + downloadUrl);
}
var resp = "{\"received\":true}".getBytes();
exchange.sendResponseHeaders(200, resp.length);
exchange.getResponseBody().write(resp);
exchange.close();
});
server.start();
System.out.println("Webhook listener on :3000");
}
}
```
```csharp
using System.Text.Json;
var app = WebApplication.Create();
app.MapPost("/webhooks/huntapi", async (HttpContext ctx) =>
{
using var reader = new StreamReader(ctx.Request.Body);
var body = await reader.ReadToEndAsync();
var payload = JsonDocument.Parse(body).RootElement;
var jobId = payload.GetProperty("job_id").GetString();
var status = payload.GetProperty("status").GetString();
var downloadUrl = payload.TryGetProperty("download_url", out var d) ? d.GetString() : null;
Console.WriteLine($"Webhook received — Job: {jobId}, Status: {status}");
if (status == "done" && downloadUrl != null)
Console.WriteLine($" Download URL: {downloadUrl}");
return Results.Json(new { received = true });
});
Console.WriteLine("Webhook listener on :3000");
app.Run("http://0.0.0.0:3000");
```
```rust
use axum::{extract::Json as AxumJson, routing::post, Router};
use serde_json::{json, Value};
async fn huntapi_webhook(AxumJson(payload): AxumJson) -> AxumJson {
let job_id = payload["job_id"].as_str().unwrap_or("");
let status = payload["status"].as_str().unwrap_or("");
let download_url = payload["download_url"].as_str().unwrap_or("");
println!("Webhook received — Job: {}, Status: {}", job_id, status);
if status == "done" && !download_url.is_empty() {
println!(" Download URL: {}", download_url);
}
AxumJson(json!({ "received": true }))
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/webhooks/huntapi", post(huntapi_webhook));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("Webhook listener on :3000");
axum::serve(listener, app).await.unwrap();
}
```
### Test locally with ngrok [#test-locally-with-ngrok]
During development, use ngrok to expose your local server to the internet so HuntAPI can reach your webhook.
```bash
# Start your local server first, then:
ngrok http 3000
```
Copy the generated `https://xxxx.ngrok.io` URL and use it as your `webhook_url` parameter.
Your webhook endpoint must return a `2xx` status code within 10 seconds, otherwise HuntAPI will retry the delivery. Make heavy processing asynchronous and acknowledge the webhook immediately.
# Find a Professional Email (/playbooks/piloterr-email-finder)
## Overview [#overview]
This playbook shows how to find and verify a professional email address for a prospect. Use it in outbound sales pipelines to auto-enrich contact records before sending a sequence.
## Prerequisites [#prerequisites]
* A Piloterr API key — get one at [app.piloterr.com](https://app.piloterr.com)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Find an email address [#find-an-email-address]
Call `GET /v2/email/finder` with `first_name`, `last_name`, and `domain`.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.piloterr.com/v2/email/finder",
headers={"x-api-key": API_KEY},
params={"first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com"},
)
result = response.json()
print(result)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ first_name: "Patrick", last_name: "Collison", domain: "stripe.com" });
const response = await fetch(`https://api.piloterr.com/v2/email/finder?${params}`, {
headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
```
```php
"Patrick", "last_name" => "Collison", "domain" => "stripe.com"]);
$ch = curl_init("https://api.piloterr.com/v2/email/finder?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{
"first_name": {"Patrick"}, "last_name": {"Collison"}, "domain": {"stripe.com"},
}
req, _ := http.NewRequest("GET", "https://api.piloterr.com/v2/email/finder?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
fmt.Println(result)
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.piloterr.com/v2/email/finder?first_name=Patrick&last_name=Collison&domain=stripe.com"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync(
"https://api.piloterr.com/v2/email/finder?first_name=Patrick&last_name=Collison&domain=stripe.com");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let result = reqwest::Client::new()
.get("https://api.piloterr.com/v2/email/finder")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("first_name", "Patrick"), ("last_name", "Collison"), ("domain", "stripe.com")])
.send().await?.json::().await?;
println!("{:#?}", result);
Ok(())
}
```
### Inspect the result [#inspect-the-result]
The response includes `email`, `confidence` (0–100), `status` (`valid`, `risky`, `invalid`), and the detected email pattern.
```python
email = result.get("email")
confidence = result.get("confidence")
status = result.get("status")
if email and confidence >= 70:
print(f"✓ Found: {email} (confidence: {confidence}%, status: {status})")
else:
print(f"✗ Email not found or low confidence (status: {status})")
```
```typescript
const { email, confidence, status } = result;
if (email && confidence >= 70) {
console.log(`✓ Found: ${email} (confidence: ${confidence}%, status: ${status})`);
} else {
console.log(`✗ Email not found or low confidence (status: ${status})`);
}
```
```php
$email = $result["email"] ?? null;
$confidence = $result["confidence"] ?? 0;
$status = $result["status"] ?? "unknown";
if ($email && $confidence >= 70) {
echo "✓ Found: {$email} (confidence: {$confidence}%, status: {$status})\n";
} else {
echo "✗ Email not found or low confidence (status: {$status})\n";
}
```
```go
email := result["email"]
confidence := result["confidence"]
status := result["status"]
if email != nil && confidence.(float64) >= 70 {
fmt.Printf("✓ Found: %v (confidence: %.0f%%, status: %v)\n", email, confidence, status)
} else {
fmt.Printf("✗ Email not found or low confidence (status: %v)\n", status)
}
```
```java
import org.json.*;
var r = new JSONObject(response.body());
var email = r.optString("email", null);
var confidence = r.optInt("confidence", 0);
var status = r.optString("status", "unknown");
if (email != null && confidence >= 70) {
System.out.printf("✓ Found: %s (confidence: %d%%, status: %s)%n", email, confidence, status);
} else {
System.out.printf("✗ Email not found or low confidence (status: %s)%n", status);
}
```
```csharp
using System.Text.Json;
var r = JsonDocument.Parse(body).RootElement;
var email = r.TryGetProperty("email", out var e) ? e.GetString() : null;
var confidence = r.TryGetProperty("confidence", out var c) ? c.GetInt32() : 0;
var status = r.TryGetProperty("status", out var s) ? s.GetString() : "unknown";
if (email != null && confidence >= 70)
Console.WriteLine($"✓ Found: {email} (confidence: {confidence}%, status: {status})");
else
Console.WriteLine($"✗ Email not found or low confidence (status: {status})");
```
```rust
let email = result["email"].as_str().unwrap_or("");
let confidence = result["confidence"].as_i64().unwrap_or(0);
let status = result["status"].as_str().unwrap_or("unknown");
if !email.is_empty() && confidence >= 70 {
println!("✓ Found: {} (confidence: {}%, status: {})", email, confidence, status);
} else {
println!("✗ Email not found or low confidence (status: {})", status);
}
```
### Enrich a list of prospects from a CSV [#enrich-a-list-of-prospects-from-a-csv]
Load a CSV of prospects, find their emails, and write results back out.
```python
import csv, time, requests
API_KEY = "YOUR_API_KEY"
def find_email(first: str, last: str, domain: str) -> dict:
r = requests.get("https://api.piloterr.com/v2/email/finder",
headers={"x-api-key": API_KEY},
params={"first_name": first, "last_name": last, "domain": domain})
return r.json()
prospects = [
{"first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com"},
{"first_name": "Sam", "last_name": "Altman", "domain": "openai.com"},
{"first_name": "Tobi", "last_name": "Lutke", "domain": "shopify.com"},
]
with open("prospects_enriched.csv", "w", newline="") as f:
fieldnames = ["first_name", "last_name", "domain", "email", "confidence", "status"]
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for p in prospects:
result = find_email(p["first_name"], p["last_name"], p["domain"])
writer.writerow({**p, "email": result.get("email", ""), "confidence": result.get("confidence", 0), "status": result.get("status", "")})
time.sleep(0.5)
print("Done! Results saved to prospects_enriched.csv")
```
```typescript
import { createWriteStream } from "fs";
const API_KEY = "YOUR_API_KEY";
const prospects = [
{ first_name: "Patrick", last_name: "Collison", domain: "stripe.com" },
{ first_name: "Sam", last_name: "Altman", domain: "openai.com" },
{ first_name: "Tobi", last_name: "Lutke", domain: "shopify.com" },
];
const rows: string[] = ["first_name,last_name,domain,email,confidence,status"];
for (const p of prospects) {
const params = new URLSearchParams({ first_name: p.first_name, last_name: p.last_name, domain: p.domain });
const res = await fetch(`https://api.piloterr.com/v2/email/finder?${params}`, {
headers: { "x-api-key": API_KEY },
});
const r = await res.json();
rows.push(`${p.first_name},${p.last_name},${p.domain},${r.email ?? ""},${r.confidence ?? 0},${r.status ?? ""}`);
await new Promise(resolve => setTimeout(resolve, 500));
}
import { writeFileSync } from "fs";
writeFileSync("prospects_enriched.csv", rows.join("\n"));
console.log("Done! Results saved to prospects_enriched.csv");
```
```php
"Patrick", "last_name" => "Collison", "domain" => "stripe.com"],
["first_name" => "Sam", "last_name" => "Altman", "domain" => "openai.com"],
["first_name" => "Tobi", "last_name" => "Lutke", "domain" => "shopify.com"],
];
$fp = fopen("prospects_enriched.csv", "w");
fputcsv($fp, ["first_name", "last_name", "domain", "email", "confidence", "status"]);
foreach ($prospects as $p) {
$params = http_build_query(array_merge($p, ["query" => ""]));
$ch = curl_init("https://api.piloterr.com/v2/email/finder?" . http_build_query($p));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$r = json_decode(curl_exec($ch), true);
curl_close($ch);
fputcsv($fp, [$p["first_name"], $p["last_name"], $p["domain"],
$r["email"] ?? "", $r["confidence"] ?? 0, $r["status"] ?? ""]);
usleep(500000);
}
fclose($fp);
echo "Done! Results saved to prospects_enriched.csv\n";
```
```go
package main
import (
"encoding/csv"
"encoding/json"
"io"
"net/http"
"net/url"
"os"
"strconv"
"time"
)
type Prospect struct{ FirstName, LastName, Domain string }
func findEmail(apiKey string, p Prospect) map[string]any {
params := url.Values{"first_name": {p.FirstName}, "last_name": {p.LastName}, "domain": {p.Domain}}
req, _ := http.NewRequest("GET", "https://api.piloterr.com/v2/email/finder?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var r map[string]any
json.Unmarshal(body, &r)
return r
}
func main() {
apiKey := "YOUR_API_KEY"
prospects := []Prospect{
{"Patrick", "Collison", "stripe.com"},
{"Sam", "Altman", "openai.com"},
{"Tobi", "Lutke", "shopify.com"},
}
f, _ := os.Create("prospects_enriched.csv")
w := csv.NewWriter(f)
w.Write([]string{"first_name", "last_name", "domain", "email", "confidence", "status"})
for _, p := range prospects {
r := findEmail(apiKey, p)
confidence := strconv.FormatFloat(r["confidence"].(float64), 'f', 0, 64)
w.Write([]string{p.FirstName, p.LastName, p.Domain,
r["email"].(string), confidence, r["status"].(string)})
time.Sleep(500 * time.Millisecond)
}
w.Flush()
f.Close()
}
```
```java
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var apiKey = "YOUR_API_KEY";
var prospects = List.of(
Map.of("first_name","Patrick","last_name","Collison","domain","stripe.com"),
Map.of("first_name","Sam", "last_name","Altman", "domain","openai.com"),
Map.of("first_name","Tobi", "last_name","Lutke", "domain","shopify.com"));
var lines = new ArrayList();
lines.add("first_name,last_name,domain,email,confidence,status");
for (var p : prospects) {
var url = "https://api.piloterr.com/v2/email/finder?first_name=" + p.get("first_name")
+ "&last_name=" + p.get("last_name") + "&domain=" + p.get("domain");
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var r = new JSONObject(resp.body());
lines.add(String.join(",", p.get("first_name"), p.get("last_name"), p.get("domain"),
r.optString("email",""), String.valueOf(r.optInt("confidence")), r.optString("status","")));
Thread.sleep(500);
}
Files.write(Path.of("prospects_enriched.csv"), lines);
System.out.println("Done! Results saved to prospects_enriched.csv");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var prospects = new[] {
(first: "Patrick", last: "Collison", domain: "stripe.com"),
(first: "Sam", last: "Altman", domain: "openai.com"),
(first: "Tobi", last: "Lutke", domain: "shopify.com"),
};
var lines = new List { "first_name,last_name,domain,email,confidence,status" };
foreach (var p in prospects)
{
var body = await client.GetStringAsync(
$"https://api.piloterr.com/v2/email/finder?first_name={p.first}&last_name={p.last}&domain={p.domain}");
var r = JsonDocument.Parse(body).RootElement;
var email = r.TryGetProperty("email", out var e) ? e.GetString() : "";
var confidence = r.TryGetProperty("confidence", out var c) ? c.GetInt32().ToString() : "0";
var status = r.TryGetProperty("status", out var s) ? s.GetString() : "";
lines.Add($"{p.first},{p.last},{p.domain},{email},{confidence},{status}");
await Task.Delay(500);
}
File.WriteAllLines("prospects_enriched.csv", lines);
Console.WriteLine("Done! Results saved to prospects_enriched.csv");
```
```rust
use reqwest::Client;
use serde_json::Value;
use std::{fs::File, io::Write, time::Duration};
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let prospects = vec![
("Patrick", "Collison", "stripe.com"),
("Sam", "Altman", "openai.com"),
("Tobi", "Lutke", "shopify.com"),
];
let mut file = File::create("prospects_enriched.csv").unwrap();
writeln!(file, "first_name,last_name,domain,email,confidence,status").unwrap();
for (first, last, domain) in &prospects {
let r = client.get("https://api.piloterr.com/v2/email/finder")
.header("x-api-key", api_key)
.query(&[("first_name", first), ("last_name", last), ("domain", domain)])
.send().await?.json::().await?;
writeln!(file, "{},{},{},{},{},{}",
first, last, domain,
r["email"].as_str().unwrap_or(""),
r["confidence"].as_i64().unwrap_or(0),
r["status"].as_str().unwrap_or("")).unwrap();
sleep(Duration::from_millis(500)).await;
}
println!("Done! Results saved to prospects_enriched.csv");
Ok(())
}
```
Only use emails with `confidence >= 70` and `status == "valid"` in cold outreach. Lower-confidence addresses risk bounces that harm your domain reputation.
# Enrich a Lead with LinkedIn (/playbooks/piloterr-linkedin-enrichment)
## Overview [#overview]
This playbook builds a **CRM enrichment pipeline**: given a company's website domain, fetch its LinkedIn profile and extract structured data like industry, employee count, headquarters, and specialities.
## Prerequisites [#prerequisites]
* A Piloterr API key — get one at [app.piloterr.com](https://app.piloterr.com)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default in most PHP installs).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Look up a company by domain [#look-up-a-company-by-domain]
Pass the company's website domain to the `domain` parameter. You can also use a LinkedIn URL via `query`.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.piloterr.com/v2/linkedin/company/info",
headers={"x-api-key": API_KEY},
params={"domain": "stripe.com"},
)
company = response.json()
print(company)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ domain: "stripe.com" });
const response = await fetch(`https://api.piloterr.com/v2/linkedin/company/info?${params}`, {
headers: { "x-api-key": API_KEY },
});
const company = await response.json();
console.log(company);
```
```php
"stripe.com"]);
$ch = curl_init("https://api.piloterr.com/v2/linkedin/company/info?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$company = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($company);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET",
"https://api.piloterr.com/v2/linkedin/company/info?domain=stripe.com", nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var company map[string]any
json.Unmarshal(body, &company)
fmt.Println(company)
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.piloterr.com/v2/linkedin/company/info?domain=stripe.com"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync(
"https://api.piloterr.com/v2/linkedin/company/info?domain=stripe.com");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let company = reqwest::Client::new()
.get("https://api.piloterr.com/v2/linkedin/company/info")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("domain", "stripe.com")])
.send().await?.json::().await?;
println!("{:#?}", company);
Ok(())
}
```
### Extract key company fields [#extract-key-company-fields]
The response contains `company_name`, `industry`, `staff_count`, `staff_range`, `tagline`, `description`, `headquarter`, and `specialities`.
```python
print(f"Company : {company.get('company_name')}")
print(f"Industry : {company.get('industry')}")
print(f"Employees : {company.get('staff_count')} ({company.get('staff_range')})")
print(f"Website : {company.get('website')}")
hq = company.get("headquarter", {})
print(f"HQ : {hq.get('city')}, {hq.get('country')}")
print(f"Topics : {', '.join(company.get('specialities', [])[:5])}")
```
```typescript
console.log(`Company : ${company.company_name}`);
console.log(`Industry : ${company.industry}`);
console.log(`Employees : ${company.staff_count} (${company.staff_range})`);
console.log(`Website : ${company.website}`);
console.log(`HQ : ${company.headquarter?.city}, ${company.headquarter?.country}`);
console.log(`Topics : ${(company.specialities ?? []).slice(0, 5).join(", ")}`);
```
```php
echo "Company : {$company['company_name']}\n";
echo "Industry : {$company['industry']}\n";
echo "Employees : {$company['staff_count']} ({$company['staff_range']})\n";
echo "HQ : {$company['headquarter']['city']}, {$company['headquarter']['country']}\n";
echo "Topics : " . implode(", ", array_slice($company["specialities"] ?? [], 0, 5)) . "\n";
```
```go
c := company
fmt.Printf("Company : %v\nIndustry : %v\nEmployees : %v (%v)\nWebsite : %v\n",
c["company_name"], c["industry"], c["staff_count"], c["staff_range"], c["website"])
if hq, ok := c["headquarter"].(map[string]any); ok {
fmt.Printf("HQ : %v, %v\n", hq["city"], hq["country"])
}
```
```java
import org.json.*;
var c = new JSONObject(response.body());
System.out.printf("Company : %s%nIndustry : %s%nEmployees : %d (%s)%nWebsite : %s%n",
c.getString("company_name"), c.getString("industry"),
c.getInt("staff_count"), c.getString("staff_range"), c.getString("website"));
var hq = c.optJSONObject("headquarter");
if (hq != null) System.out.printf("HQ : %s, %s%n", hq.getString("city"), hq.getString("country"));
```
```csharp
using System.Text.Json;
var c = JsonDocument.Parse(body).RootElement;
Console.WriteLine($"Company : {c.GetProperty("company_name")}");
Console.WriteLine($"Industry : {c.GetProperty("industry")}");
Console.WriteLine($"Employees : {c.GetProperty("staff_count")} ({c.GetProperty("staff_range")})");
Console.WriteLine($"Website : {c.GetProperty("website")}");
var hq = c.GetProperty("headquarter");
Console.WriteLine($"HQ : {hq.GetProperty("city")}, {hq.GetProperty("country")}");
```
```rust
let c = &company;
println!("Company : {}", c["company_name"].as_str().unwrap_or(""));
println!("Industry : {}", c["industry"].as_str().unwrap_or(""));
println!("Employees : {} ({})", c["staff_count"], c["staff_range"].as_str().unwrap_or(""));
println!("HQ : {}, {}", c["headquarter"]["city"].as_str().unwrap_or(""), c["headquarter"]["country"].as_str().unwrap_or(""));
```
### Enrich a batch of leads [#enrich-a-batch-of-leads]
Loop over a list of email domains and build enriched company records ready to push to your CRM.
```python
import json, time, requests
API_KEY = "YOUR_API_KEY"
leads = [
{"email": "alice@stripe.com", "domain": "stripe.com"},
{"email": "bob@notion.so", "domain": "notion.so"},
{"email": "carol@figma.com", "domain": "figma.com"},
]
enriched = []
for lead in leads:
r = requests.get("https://api.piloterr.com/v2/linkedin/company/info",
headers={"x-api-key": API_KEY}, params={"domain": lead["domain"]})
if r.status_code == 200:
c = r.json()
enriched.append({"email": lead["email"], "domain": lead["domain"],
"company": c.get("company_name"), "industry": c.get("industry"),
"employees": c.get("staff_count"), "hq_country": c.get("headquarter", {}).get("country"),
"linkedin_url": c.get("company_url")})
time.sleep(0.5)
with open("enriched_leads.json", "w") as f:
json.dump(enriched, f, indent=2)
print(f"Enriched {len(enriched)} leads → enriched_leads.json")
```
```typescript
import { writeFileSync } from "fs";
const API_KEY = "YOUR_API_KEY";
const leads = [
{ email: "alice@stripe.com", domain: "stripe.com" },
{ email: "bob@notion.so", domain: "notion.so" },
{ email: "carol@figma.com", domain: "figma.com" },
];
const enriched: any[] = [];
for (const lead of leads) {
const params = new URLSearchParams({ domain: lead.domain });
const res = await fetch(`https://api.piloterr.com/v2/linkedin/company/info?${params}`, {
headers: { "x-api-key": API_KEY },
});
if (res.ok) {
const c = await res.json();
enriched.push({ email: lead.email, domain: lead.domain, company: c.company_name,
industry: c.industry, employees: c.staff_count, hq_country: c.headquarter?.country,
linkedin_url: c.company_url });
}
await new Promise(r => setTimeout(r, 500));
}
writeFileSync("enriched_leads.json", JSON.stringify(enriched, null, 2));
console.log(`Enriched ${enriched.length} leads → enriched_leads.json`);
```
```php
"alice@stripe.com", "domain" => "stripe.com"],
["email" => "bob@notion.so", "domain" => "notion.so"],
["email" => "carol@figma.com", "domain" => "figma.com"],
];
$enriched = [];
foreach ($leads as $lead) {
$params = http_build_query(["domain" => $lead["domain"]]);
$ch = curl_init("https://api.piloterr.com/v2/linkedin/company/info?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$c = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
$enriched[] = ["email" => $lead["email"], "domain" => $lead["domain"],
"company" => $c["company_name"] ?? null, "industry" => $c["industry"] ?? null,
"employees" => $c["staff_count"] ?? null, "hq_country" => $c["headquarter"]["country"] ?? null];
}
curl_close($ch);
usleep(500000);
}
file_put_contents("enriched_leads.json", json_encode($enriched, JSON_PRETTY_PRINT));
echo "Enriched " . count($enriched) . " leads → enriched_leads.json\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
func enrichDomain(apiKey, domain string) map[string]any {
req, _ := http.NewRequest("GET",
"https://api.piloterr.com/v2/linkedin/company/info?domain="+domain, nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var c map[string]any
json.Unmarshal(body, &c)
return c
}
func main() {
apiKey := "YOUR_API_KEY"
leads := []struct{ Email, Domain string }{
{"alice@stripe.com", "stripe.com"},
{"bob@notion.so", "notion.so"},
{"carol@figma.com", "figma.com"},
}
var enriched []map[string]any
for _, lead := range leads {
c := enrichDomain(apiKey, lead.Domain)
hq, _ := c["headquarter"].(map[string]any)
enriched = append(enriched, map[string]any{
"email": lead.Email, "domain": lead.Domain,
"company": c["company_name"], "industry": c["industry"],
"employees": c["staff_count"], "hq_country": hq["country"],
})
time.Sleep(500 * time.Millisecond)
}
b, _ := json.MarshalIndent(enriched, "", " ")
os.WriteFile("enriched_leads.json", b, 0644)
fmt.Printf("Enriched %d leads → enriched_leads.json\n", len(enriched))
}
```
```java
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var apiKey = "YOUR_API_KEY";
var leads = List.of(
Map.of("email", "alice@stripe.com", "domain", "stripe.com"),
Map.of("email", "bob@notion.so", "domain", "notion.so"),
Map.of("email", "carol@figma.com", "domain", "figma.com"));
var enriched = new JSONArray();
for (var lead : leads) {
var url = "https://api.piloterr.com/v2/linkedin/company/info?domain=" + lead.get("domain");
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() == 200) {
var c = new JSONObject(resp.body());
var hq = c.optJSONObject("headquarter");
enriched.put(new JSONObject()
.put("email", lead.get("email"))
.put("domain", lead.get("domain"))
.put("company", c.optString("company_name"))
.put("industry", c.optString("industry"))
.put("employees", c.optInt("staff_count"))
.put("hq_country", hq != null ? hq.optString("country") : ""));
}
Thread.sleep(500);
}
Files.writeString(Path.of("enriched_leads.json"), enriched.toString(2));
System.out.println("Enriched " + enriched.length() + " leads → enriched_leads.json");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var leads = new[] {
(email: "alice@stripe.com", domain: "stripe.com"),
(email: "bob@notion.so", domain: "notion.so"),
(email: "carol@figma.com", domain: "figma.com"),
};
var enriched = new List
```rust
use reqwest::Client;
use serde_json::{json, Value};
use std::{fs, time::Duration};
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let leads = vec![("alice@stripe.com", "stripe.com"), ("bob@notion.so", "notion.so"), ("carol@figma.com", "figma.com")];
let mut enriched: Vec = Vec::new();
for (email, domain) in &leads {
let c = client.get("https://api.piloterr.com/v2/linkedin/company/info")
.header("x-api-key", api_key).query(&[("domain", domain)])
.send().await?.json::().await?;
enriched.push(json!({
"email": email, "domain": domain,
"company": c["company_name"], "industry": c["industry"],
"employees": c["staff_count"], "hq_country": c["headquarter"]["country"],
}));
sleep(Duration::from_millis(500)).await;
}
fs::write("enriched_leads.json", serde_json::to_string_pretty(&enriched).unwrap()).unwrap();
println!("Enriched {} leads → enriched_leads.json", enriched.len());
Ok(())
}
```
You can also pass a LinkedIn company URL or username to the `query` parameter instead of a domain — useful when you already have the LinkedIn URL from a scrape or manual research.
# Crawl Any Website (/playbooks/piloterr-website-crawler)
## Overview [#overview]
This playbook shows how to fetch the full HTML of any webpage using the Piloterr Website Crawler, then extract specific data from it. A typical use case is **competitor price monitoring**: crawl a product page daily and parse the price from the HTML.
## Prerequisites [#prerequisites]
* A Piloterr API key — get one at [app.piloterr.com](https://app.piloterr.com)
* Install dependencies for your language:
```bash
pip install requests beautifulsoup4
```
```bash
npm install node-html-parser
```
`curl` and `DOMDocument` extensions (both enabled by default).
No extra dependencies for the request — uses `net/http` (Go 1.18+). Add `golang.org/x/net/html` for parsing.
No extra dependencies for the request — uses `java.net.http` (Java 11+). Add `org.jsoup:jsoup` for parsing.
No extra dependencies for the request — uses `System.Net.Http` (.NET 6+). Add `HtmlAgilityPack` for parsing.
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Crawl a webpage [#crawl-a-webpage]
Call `GET /v2/website/crawler` with the `query` parameter set to the target URL. The response is the raw HTML string (JSON-encoded).
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.piloterr.com/v2/website/crawler",
headers={"x-api-key": API_KEY},
params={"query": "https://example.com", "allow_redirects": "true"},
)
html = response.json() # returns the HTML as a string
print(html[:500])
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ query: "https://example.com", allow_redirects: "true" });
const response = await fetch(`https://api.piloterr.com/v2/website/crawler?${params}`, {
headers: { "x-api-key": API_KEY },
});
const html: string = await response.json();
console.log(html.slice(0, 500));
```
```php
"https://example.com", "allow_redirects" => "true"]);
$ch = curl_init("https://api.piloterr.com/v2/website/crawler?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$html = json_decode(curl_exec($ch), true); // HTML string
curl_close($ch);
echo substr($html, 0, 500);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"query": {"https://example.com"}, "allow_redirects": {"true"}}
req, _ := http.NewRequest("GET", "https://api.piloterr.com/v2/website/crawler?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var html string
json.Unmarshal(body, &html) // response is a JSON-encoded string
fmt.Println(html[:500])
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.piloterr.com/v2/website/crawler?query=https%3A%2F%2Fexample.com&allow_redirects=true"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Response body is a JSON-encoded string — strip the outer quotes
var html = response.body().replaceAll("^\"|\"$", "")
.replace("\\n", "\n").replace("\\\"", "\"");
System.out.println(html.substring(0, Math.min(500, html.length())));
```
```csharp
using System.Net.Http;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var raw = await client.GetStringAsync(
"https://api.piloterr.com/v2/website/crawler?query=https%3A%2F%2Fexample.com&allow_redirects=true");
var html = JsonSerializer.Deserialize(raw)!; // response is JSON-encoded string
Console.WriteLine(html[..Math.Min(500, html.Length)]);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let html = reqwest::Client::new()
.get("https://api.piloterr.com/v2/website/crawler")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("query", "https://example.com"), ("allow_redirects", "true")])
.send().await?
.json::().await?;
println!("{}", &html[..500.min(html.len())]);
Ok(())
}
```
### Extract data from the HTML [#extract-data-from-the-html]
Parse the HTML to extract specific elements — here we extract the page title and all `
` headings.
```python
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
title = soup.find("title")
print("Page title:", title.text if title else "N/A")
for h in soup.find_all("h1"):
print("H1:", h.get_text(strip=True))
```
```typescript
import { parse } from "node-html-parser";
const root = parse(html);
const title = root.querySelector("title");
console.log("Page title:", title?.text ?? "N/A");
for (const h of root.querySelectorAll("h1")) {
console.log("H1:", h.text.trim());
}
```
```php
$dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$title = $xpath->query("//title")->item(0);
echo "Page title: " . ($title ? $title->textContent : "N/A") . "\n";
foreach ($xpath->query("//h1") as $h) {
echo "H1: " . trim($h->textContent) . "\n";
}
```
```go
import (
"fmt"
"strings"
"golang.org/x/net/html"
)
doc, _ := html.Parse(strings.NewReader(html))
var traverse func(*html.Node)
traverse = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil {
fmt.Println("Page title:", n.FirstChild.Data)
}
if n.Type == html.ElementNode && n.Data == "h1" && n.FirstChild != nil {
fmt.Println("H1:", n.FirstChild.Data)
}
for c := n.FirstChild; c != nil; c = c.NextSibling { traverse(c) }
}
traverse(doc)
```
```java
import org.jsoup.Jsoup;
var doc = Jsoup.parse(html);
System.out.println("Page title: " + doc.title());
doc.select("h1").forEach(h -> System.out.println("H1: " + h.text()));
```
```csharp
using HtmlAgilityPack;
var doc = new HtmlDocument();
doc.LoadHtml(html);
var title = doc.DocumentNode.SelectSingleNode("//title");
Console.WriteLine($"Page title: {title?.InnerText ?? "N/A"}");
foreach (var h in doc.DocumentNode.SelectNodes("//h1") ?? Enumerable.Empty())
Console.WriteLine($"H1: {h.InnerText.Trim()}");
```
```rust
// Minimal regex-based extraction
use regex::Regex; // add regex = "1" to Cargo.toml
let title_re = Regex::new(r"]*>(.*?)").unwrap();
if let Some(cap) = title_re.captures(&html) { println!("Page title: {}", &cap[1]); }
let h1_re = Regex::new(r"
]*>(.*?)
").unwrap();
for cap in h1_re.captures_iter(&html) { println!("H1: {}", &cap[1]); }
```
### Build a price monitoring script [#build-a-price-monitoring-script]
Crawl a product page and extract the price using a CSS selector.
```python
import requests
from bs4 import BeautifulSoup
API_KEY = "YOUR_API_KEY"
WATCH_URL = "https://www.example-shop.com/product/123"
def crawl(url: str) -> str:
r = requests.get("https://api.piloterr.com/v2/website/crawler",
headers={"x-api-key": API_KEY}, params={"query": url, "allow_redirects": "true"})
return r.json()
def extract_price(html: str) -> str | None:
soup = BeautifulSoup(html, "html.parser")
el = soup.select_one("[data-price], .price, #price")
return el.get_text(strip=True) if el else None
price = extract_price(crawl(WATCH_URL))
print(f"Current price: {price}" if price else "Price element not found.")
```
```typescript
import { parse } from "node-html-parser";
const API_KEY = "YOUR_API_KEY";
const WATCH_URL = "https://www.example-shop.com/product/123";
async function crawl(url: string): Promise {
const params = new URLSearchParams({ query: url, allow_redirects: "true" });
const res = await fetch(`https://api.piloterr.com/v2/website/crawler?${params}`, {
headers: { "x-api-key": API_KEY },
});
return res.json();
}
const html = await crawl(WATCH_URL);
const root = parse(html);
const price = root.querySelector("[data-price], .price, #price")?.text.trim() ?? null;
console.log(price ? `Current price: ${price}` : "Price element not found.");
```
```php
$url, "allow_redirects" => "true"]);
$ch = curl_init("https://api.piloterr.com/v2/website/crawler?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$html = json_decode(curl_exec($ch), true);
curl_close($ch);
return $html;
}
$html = crawl("YOUR_API_KEY", "https://www.example-shop.com/product/123");
$dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$price = null;
foreach (["//span[@class='price']", "//*[@id='price']", "//*[@data-price]"] as $sel) {
$nodes = $xpath->query($sel);
if ($nodes->length > 0) { $price = trim($nodes->item(0)->textContent); break; }
}
echo $price ? "Current price: {$price}\n" : "Price element not found.\n";
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"regexp"
)
func crawl(apiKey, pageUrl string) string {
params := url.Values{"query": {pageUrl}, "allow_redirects": {"true"}}
req, _ := http.NewRequest("GET", "https://api.piloterr.com/v2/website/crawler?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var html string
json.Unmarshal(body, &html)
return html
}
func main() {
html := crawl("YOUR_API_KEY", "https://www.example-shop.com/product/123")
re := regexp.MustCompile(`class="price[^"]*"[^>]*>([^<]+)`)
match := re.FindStringSubmatch(html)
if match != nil {
fmt.Println("Current price:", match[1])
} else {
fmt.Println("Price element not found.")
}
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.jsoup.Jsoup;
public class Main {
public static void main(String[] args) throws Exception {
var apiKey = "YOUR_API_KEY";
var watchUrl = URLEncoder.encode("https://www.example-shop.com/product/123", StandardCharsets.UTF_8);
var url = "https://api.piloterr.com/v2/website/crawler?query=" + watchUrl + "&allow_redirects=true";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", apiKey).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Strip JSON string quotes
var html = response.body().replaceAll("^\"|\"$", "").replace("\\\"", "\"").replace("\\n", "\n");
var doc = Jsoup.parse(html);
var priceEl = doc.selectFirst(".price, #price, [data-price]");
System.out.println(priceEl != null ? "Current price: " + priceEl.text() : "Price element not found.");
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
using HtmlAgilityPack;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var watchUrl = Uri.EscapeDataString("https://www.example-shop.com/product/123");
var raw = await client.GetStringAsync(
$"https://api.piloterr.com/v2/website/crawler?query={watchUrl}&allow_redirects=true");
var html = JsonSerializer.Deserialize(raw)!;
var doc = new HtmlDocument();
doc.LoadHtml(html);
var price = doc.DocumentNode.SelectSingleNode("//*[contains(@class,'price') or @id='price' or @data-price]");
Console.WriteLine(price != null ? $"Current price: {price.InnerText.Trim()}" : "Price element not found.");
```
```rust
use reqwest::Client;
use regex::Regex;
async fn crawl(client: &Client, api_key: &str, url: &str) -> String {
client.get("https://api.piloterr.com/v2/website/crawler")
.header("x-api-key", api_key)
.query(&[("query", url), ("allow_redirects", "true")])
.send().await.unwrap().json::().await.unwrap()
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let html = crawl(&client, "YOUR_API_KEY", "https://www.example-shop.com/product/123").await;
let re = Regex::new(r#"class="price[^"]*"[^>]*>([^<]+)"#).unwrap();
match re.captures(&html) {
Some(cap) => println!("Current price: {}", cap[1].trim()),
None => println!("Price element not found."),
}
Ok(())
}
```
Set `allow_redirects=true` to follow HTTP 301/302 redirects automatically — useful for short URLs or e-commerce platforms that redirect product pages.
# Detect Disposable Domains (/playbooks/veille-domain-validation)
## Overview [#overview]
Disposable email providers let users create temporary inboxes that get deleted after minutes or days. This playbook shows how to query the Veille domain validation endpoint and use the result to block or flag signups at the point of registration.
## Prerequisites [#prerequisites]
* A Veille API key — get one at [app.veille.io](https://app.veille.io)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Check a domain [#check-a-domain]
Call `GET /v1/domain` with the `domain` parameter. The response returns whether it is disposable, free, or a custom domain.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.veille.io/v1/domain",
headers={"x-api-key": API_KEY},
params={"domain": "mailinator.com"},
)
result = response.json()
print(result)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ domain: "mailinator.com" });
const response = await fetch(`https://api.veille.io/v1/domain?${params}`, {
headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
```
```php
"mailinator.com"]);
$ch = curl_init("https://api.veille.io/v1/domain?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/domain?domain=mailinator.com", nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
fmt.Println(result)
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/domain?domain=mailinator.com"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync("https://api.veille.io/v1/domain?domain=mailinator.com");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let result = reqwest::Client::new()
.get("https://api.veille.io/v1/domain")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("domain", "mailinator.com")])
.send().await?.json::().await?;
println!("{:#?}", result);
Ok(())
}
```
### Read the response fields [#read-the-response-fields]
The response includes `is_disposable`, `is_free`, `is_custom`, `domain`, and `provider` (when identified).
```python
domain = result.get("domain")
is_disposable = result.get("is_disposable")
is_free = result.get("is_free")
provider = result.get("provider", "unknown")
if is_disposable:
print(f"❌ {domain} is a DISPOSABLE email provider ({provider}). Block this signup.")
elif is_free:
print(f"⚠️ {domain} is a free email provider. Consider extra verification.")
else:
print(f"✅ {domain} looks like a custom / business domain. Allow.")
```
```typescript
const { domain, is_disposable, is_free, provider } = result;
if (is_disposable) {
console.log(`❌ ${domain} is DISPOSABLE (${provider ?? "unknown"}). Block this signup.`);
} else if (is_free) {
console.log(`⚠️ ${domain} is a free provider. Consider extra verification.`);
} else {
console.log(`✅ ${domain} looks like a business domain. Allow.`);
}
```
```php
$domain = $result["domain"] ?? "";
$isDisposable = $result["is_disposable"] ?? false;
$isFree = $result["is_free"] ?? false;
$provider = $result["provider"] ?? "unknown";
if ($isDisposable) {
echo "❌ {$domain} is DISPOSABLE ({$provider}). Block this signup.\n";
} elseif ($isFree) {
echo "⚠️ {$domain} is free. Consider extra verification.\n";
} else {
echo "✅ {$domain} looks like a business domain. Allow.\n";
}
```
```go
domain := result["domain"].(string)
isDisposable := result["is_disposable"].(bool)
isFree := result["is_free"].(bool)
provider, _ := result["provider"].(string)
switch {
case isDisposable:
fmt.Printf("❌ %s is DISPOSABLE (%s). Block this signup.\n", domain, provider)
case isFree:
fmt.Printf("⚠️ %s is a free provider.\n", domain)
default:
fmt.Printf("✅ %s is a business domain. Allow.\n", domain)
}
```
```java
import org.json.*;
var r = new JSONObject(response.body());
var domain = r.getString("domain");
var isDisposable = r.getBoolean("is_disposable");
var isFree = r.getBoolean("is_free");
var provider = r.optString("provider", "unknown");
if (isDisposable) System.out.printf("❌ %s is DISPOSABLE (%s). Block.%n", domain, provider);
else if (isFree) System.out.printf("⚠️ %s is free. Extra verification.%n", domain);
else System.out.printf("✅ %s is a business domain. Allow.%n", domain);
```
```csharp
using System.Text.Json;
var r = JsonDocument.Parse(body).RootElement;
var domain = r.GetProperty("domain").GetString();
var isDisposable = r.GetProperty("is_disposable").GetBoolean();
var isFree = r.GetProperty("is_free").GetBoolean();
var provider = r.TryGetProperty("provider", out var p) ? p.GetString() : "unknown";
if (isDisposable)
Console.WriteLine($"❌ {domain} is DISPOSABLE ({provider}). Block.");
else if (isFree)
Console.WriteLine($"⚠️ {domain} is free. Extra verification.");
else
Console.WriteLine($"✅ {domain} is a business domain. Allow.");
```
```rust
let domain = result["domain"].as_str().unwrap_or("");
let is_disposable = result["is_disposable"].as_bool().unwrap_or(false);
let is_free = result["is_free"].as_bool().unwrap_or(false);
let provider = result["provider"].as_str().unwrap_or("unknown");
if is_disposable {
println!("❌ {} is DISPOSABLE ({}). Block.", domain, provider);
} else if is_free {
println!("⚠️ {} is free. Extra verification.", domain);
} else {
println!("✅ {} is a business domain. Allow.", domain);
}
```
### Integrate into a signup handler [#integrate-into-a-signup-handler]
Add the domain check to your registration endpoint and return a validation error before the user record is created.
```python
import requests
API_KEY = "YOUR_API_KEY"
def is_disposable_email(email: str) -> bool:
domain = email.split("@")[-1].lower()
r = requests.get("https://api.veille.io/v1/domain",
headers={"x-api-key": API_KEY}, params={"domain": domain})
return r.json().get("is_disposable", False)
def register_user(email: str, password: str) -> dict:
if is_disposable_email(email):
return {"success": False, "error": "Disposable email addresses are not allowed."}
# ... create the user record in your database
return {"success": True, "message": f"Welcome, {email}!"}
print(register_user("alice@mailinator.com", "secret"))
print(register_user("alice@company.com", "secret"))
```
```typescript
const API_KEY = "YOUR_API_KEY";
async function isDisposableEmail(email: string): Promise {
const domain = email.split("@").at(-1)!.toLowerCase();
const params = new URLSearchParams({ domain });
const res = await fetch(`https://api.veille.io/v1/domain?${params}`, {
headers: { "x-api-key": API_KEY },
});
return (await res.json()).is_disposable ?? false;
}
async function registerUser(email: string, password: string) {
if (await isDisposableEmail(email)) {
return { success: false, error: "Disposable email addresses are not allowed." };
}
// ... create user record
return { success: true, message: `Welcome, ${email}!` };
}
console.log(await registerUser("alice@mailinator.com", "secret"));
console.log(await registerUser("alice@company.com", "secret"));
```
```php
$domain]);
$ch = curl_init("https://api.veille.io/v1/domain?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
return $result["is_disposable"] ?? false;
}
function registerUser(string $apiKey, string $email, string $password): array {
if (isDisposableEmail($apiKey, $email)) {
return ["success" => false, "error" => "Disposable email addresses are not allowed."];
}
// ... create user record
return ["success" => true, "message" => "Welcome, {$email}!"];
}
print_r(registerUser("YOUR_API_KEY", "alice@mailinator.com", "secret"));
print_r(registerUser("YOUR_API_KEY", "alice@company.com", "secret"));
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
func isDisposable(apiKey, email string) bool {
domain := strings.ToLower(strings.SplitN(email, "@", 2)[1])
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/domain?domain="+domain, nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
v, _ := result["is_disposable"].(bool)
return v
}
func registerUser(apiKey, email, password string) string {
if isDisposable(apiKey, email) {
return "❌ Disposable email not allowed."
}
return "✅ Welcome, " + email + "!"
}
func main() {
apiKey := "YOUR_API_KEY"
fmt.Println(registerUser(apiKey, "alice@mailinator.com", "secret"))
fmt.Println(registerUser(apiKey, "alice@company.com", "secret"))
}
```
```java
import java.net.URI;
import java.net.http.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
static boolean isDisposable(String email) throws Exception {
var domain = email.substring(email.indexOf('@') + 1).toLowerCase();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/domain?domain=" + domain))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(request, HttpResponse.BodyHandlers.ofString());
return new JSONObject(resp.body()).optBoolean("is_disposable", false);
}
static String registerUser(String email, String password) throws Exception {
if (isDisposable(email)) return "❌ Disposable email not allowed.";
return "✅ Welcome, " + email + "!";
}
public static void main(String[] args) throws Exception {
System.out.println(registerUser("alice@mailinator.com", "secret"));
System.out.println(registerUser("alice@company.com", "secret"));
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
async Task IsDisposable(string email)
{
var domain = email.Split('@').Last().ToLower();
var body = await client.GetStringAsync($"https://api.veille.io/v1/domain?domain={domain}");
return JsonDocument.Parse(body).RootElement.GetProperty("is_disposable").GetBoolean();
}
async Task RegisterUser(string email, string password) =>
await IsDisposable(email)
? "❌ Disposable email not allowed."
: $"✅ Welcome, {email}!";
Console.WriteLine(await RegisterUser("alice@mailinator.com", "secret"));
Console.WriteLine(await RegisterUser("alice@company.com", "secret"));
```
```rust
use reqwest::Client;
use serde_json::Value;
async fn is_disposable(client: &Client, api_key: &str, email: &str) -> bool {
let domain = email.split('@').last().unwrap_or("").to_lowercase();
let result = client.get("https://api.veille.io/v1/domain")
.header("x-api-key", api_key)
.query(&[("domain", domain.as_str())])
.send().await.unwrap().json::().await.unwrap();
result["is_disposable"].as_bool().unwrap_or(false)
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
for email in ["alice@mailinator.com", "alice@company.com"] {
if is_disposable(&client, api_key, email).await {
println!("❌ {} — disposable. Block.", email);
} else {
println!("✅ {} — looks valid. Allow.", email);
}
}
Ok(())
}
```
Cache results for frequently checked domains (e.g. in Redis with a 24-hour TTL) to avoid redundant API calls. The list of disposable providers rarely changes within a single day.
# Validate Email on Signup (/playbooks/veille-email-validation)
## Overview [#overview]
This playbook shows how to run a full email validation before accepting a signup: syntax check, DNS/MX record lookup, and SMTP reachability. The API returns a `risk_score` (0 = clean, 100 = high risk) that you can use to route users to extra verification steps.
## Prerequisites [#prerequisites]
* A Veille API key — get one at [app.veille.io](https://app.veille.io)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Validate an email address [#validate-an-email-address]
Call `GET /v1/email` with the `email` parameter.
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.veille.io/v1/email",
headers={"x-api-key": API_KEY},
params={"email": "alice@example.com"},
)
result = response.json()
print(result)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ email: "alice@example.com" });
const response = await fetch(`https://api.veille.io/v1/email?${params}`, {
headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
```
```php
"alice@example.com"]);
$ch = curl_init("https://api.veille.io/v1/email?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"email": {"alice@example.com"}}
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/email?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
fmt.Println(result)
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
var client = HttpClient.newHttpClient();
var email = URLEncoder.encode("alice@example.com", StandardCharsets.UTF_8);
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/email?email=" + email))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var email = Uri.EscapeDataString("alice@example.com");
var body = await client.GetStringAsync($"https://api.veille.io/v1/email?email={email}");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let result = reqwest::Client::new()
.get("https://api.veille.io/v1/email")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("email", "alice@example.com")])
.send().await?.json::().await?;
println!("{:#?}", result);
Ok(())
}
```
### Interpret the result [#interpret-the-result]
Key fields: `is_valid`, `is_deliverable`, `is_disposable`, `is_role_account`, `risk_score`, `did_you_mean` (typo suggestion).
```python
print(f"Valid : {result['is_valid']}")
print(f"Deliverable : {result['is_deliverable']}")
print(f"Disposable : {result['is_disposable']}")
print(f"Role account: {result['is_role_account']}")
print(f"Risk score : {result['risk_score']}/100")
if result.get("did_you_mean"):
print(f"Did you mean: {result['did_you_mean']}?")
```
```typescript
console.log(`Valid : ${result.is_valid}`);
console.log(`Deliverable : ${result.is_deliverable}`);
console.log(`Disposable : ${result.is_disposable}`);
console.log(`Role account: ${result.is_role_account}`);
console.log(`Risk score : ${result.risk_score}/100`);
if (result.did_you_mean) console.log(`Did you mean: ${result.did_you_mean}?`);
```
```php
echo "Valid : " . ($result["is_valid"] ? "true" : "false") . "\n";
echo "Deliverable : " . ($result["is_deliverable"] ? "true" : "false") . "\n";
echo "Disposable : " . ($result["is_disposable"] ? "true" : "false") . "\n";
echo "Risk score : {$result['risk_score']}/100\n";
if (!empty($result["did_you_mean"])) echo "Did you mean: {$result['did_you_mean']}?\n";
```
```go
fmt.Printf("Valid : %v\nDeliverable : %v\nDisposable : %v\nRisk score : %v/100\n",
result["is_valid"], result["is_deliverable"], result["is_disposable"], result["risk_score"])
if typo, ok := result["did_you_mean"].(string); ok && typo != "" {
fmt.Println("Did you mean:", typo+"?")
}
```
```java
import org.json.*;
var r = new JSONObject(response.body());
System.out.printf("Valid : %b%nDeliverable : %b%nDisposable : %b%nRisk score : %d/100%n",
r.getBoolean("is_valid"), r.getBoolean("is_deliverable"),
r.getBoolean("is_disposable"), r.getInt("risk_score"));
if (r.has("did_you_mean")) System.out.println("Did you mean: " + r.getString("did_you_mean") + "?");
```
```csharp
using System.Text.Json;
var r = JsonDocument.Parse(body).RootElement;
Console.WriteLine($"Valid : {r.GetProperty("is_valid").GetBoolean()}");
Console.WriteLine($"Deliverable : {r.GetProperty("is_deliverable").GetBoolean()}");
Console.WriteLine($"Disposable : {r.GetProperty("is_disposable").GetBoolean()}");
Console.WriteLine($"Risk score : {r.GetProperty("risk_score").GetInt32()}/100");
if (r.TryGetProperty("did_you_mean", out var typo) && typo.GetString() is { } t and not "")
Console.WriteLine($"Did you mean: {t}?");
```
```rust
println!("Valid : {}", result["is_valid"]);
println!("Deliverable : {}", result["is_deliverable"]);
println!("Disposable : {}", result["is_disposable"]);
println!("Risk score : {}/100", result["risk_score"]);
if let Some(typo) = result["did_you_mean"].as_str() {
if !typo.is_empty() { println!("Did you mean: {}?", typo); }
}
```
### Build a risk-tiered registration flow [#build-a-risk-tiered-registration-flow]
Use `risk_score` to route users: block high-risk addresses, prompt typo corrections, and apply extra friction to role accounts.
```python
import requests
API_KEY = "YOUR_API_KEY"
def validate_email(email: str) -> dict:
r = requests.get("https://api.veille.io/v1/email",
headers={"x-api-key": API_KEY}, params={"email": email})
return r.json()
def register(email: str) -> dict:
v = validate_email(email)
if not v.get("is_valid"):
return {"ok": False, "error": "This email address is invalid."}
if v.get("is_disposable"):
return {"ok": False, "error": "Temporary email addresses are not accepted."}
if v.get("did_you_mean"):
return {"ok": False, "suggestion": f"Did you mean {v['did_you_mean']}?"}
if v.get("risk_score", 0) >= 70:
return {"ok": False, "error": "This email has a high risk score. Please use a different address."}
if v.get("is_role_account"):
# Allow but flag for manual review
return {"ok": True, "flag": "role_account", "message": "Please verify your email."}
return {"ok": True, "message": "Registration successful!"}
for test_email in ["alice@mailinator.com", "info@company.com", "alice@gmial.com", "alice@stripe.com"]:
print(f"{test_email}: {register(test_email)}")
```
```typescript
const API_KEY = "YOUR_API_KEY";
async function validateEmail(email: string) {
const params = new URLSearchParams({ email });
const res = await fetch(`https://api.veille.io/v1/email?${params}`, {
headers: { "x-api-key": API_KEY },
});
return res.json();
}
async function register(email: string) {
const v = await validateEmail(email);
if (!v.is_valid) return { ok: false, error: "Invalid email address." };
if (v.is_disposable) return { ok: false, error: "Temporary emails not accepted." };
if (v.did_you_mean) return { ok: false, suggestion: `Did you mean ${v.did_you_mean}?` };
if (v.risk_score >= 70) return { ok: false, error: "High risk score — please use another address." };
if (v.is_role_account) return { ok: true, flag: "role_account", message: "Please verify your email." };
return { ok: true, message: "Registration successful!" };
}
for (const email of ["alice@mailinator.com", "info@company.com", "alice@gmial.com", "alice@stripe.com"]) {
console.log(email, await register(email));
}
```
```php
$email]);
$ch = curl_init("https://api.veille.io/v1/email?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
return $result;
}
function register(string $apiKey, string $email): array {
$v = validateEmail($apiKey, $email);
if (!($v["is_valid"] ?? false)) return ["ok" => false, "error" => "Invalid email."];
if ($v["is_disposable"] ?? false) return ["ok" => false, "error" => "Temporary emails not accepted."];
if (!empty($v["did_you_mean"])) return ["ok" => false, "suggestion" => "Did you mean {$v['did_you_mean']}?"];
if (($v["risk_score"] ?? 0) >= 70) return ["ok" => false, "error" => "High risk score."];
if ($v["is_role_account"] ?? false) return ["ok" => true, "flag" => "role_account"];
return ["ok" => true, "message" => "Registration successful!"];
}
foreach (["alice@mailinator.com", "info@company.com", "alice@gmial.com"] as $email) {
print_r(["email" => $email, "result" => register("YOUR_API_KEY", $email)]);
}
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
const APIKey = "YOUR_API_KEY"
func validate(email string) map[string]any {
params := url.Values{"email": {email}}
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/email?"+params.Encode(), nil)
req.Header.Set("x-api-key", APIKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var v map[string]any
json.Unmarshal(body, &v)
return v
}
func register(email string) string {
v := validate(email)
if !(v["is_valid"].(bool)) { return "❌ Invalid email." }
if v["is_disposable"].(bool) { return "❌ Disposable not accepted." }
if typo, ok := v["did_you_mean"].(string); ok && typo != "" {
return "⚠️ Did you mean " + typo + "?"
}
if v["risk_score"].(float64) >= 70 { return "❌ High risk score." }
return "✅ Registration successful!"
}
func main() {
for _, email := range []string{"alice@mailinator.com", "info@company.com", "alice@stripe.com"} {
fmt.Printf("%s: %s\n", email, register(email))
}
}
```
```java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
static JSONObject validate(String email) throws Exception {
var encoded = URLEncoder.encode(email, StandardCharsets.UTF_8);
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/email?email=" + encoded))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
return new JSONObject(resp.body());
}
static String register(String email) throws Exception {
var v = validate(email);
if (!v.getBoolean("is_valid")) return "❌ Invalid email.";
if (v.getBoolean("is_disposable")) return "❌ Disposable not accepted.";
if (v.has("did_you_mean")) return "⚠️ Did you mean " + v.getString("did_you_mean") + "?";
if (v.getInt("risk_score") >= 70) return "❌ High risk score.";
return "✅ Registration successful!";
}
public static void main(String[] args) throws Exception {
for (var email : new String[]{"alice@mailinator.com", "info@company.com", "alice@stripe.com"}) {
System.out.printf("%s: %s%n", email, register(email));
}
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
async Task Validate(string email)
{
var encoded = Uri.EscapeDataString(email);
var body = await client.GetStringAsync($"https://api.veille.io/v1/email?email={encoded}");
return JsonDocument.Parse(body).RootElement;
}
async Task Register(string email)
{
var v = await Validate(email);
if (!v.GetProperty("is_valid").GetBoolean()) return "❌ Invalid email.";
if (v.GetProperty("is_disposable").GetBoolean()) return "❌ Disposable not accepted.";
if (v.TryGetProperty("did_you_mean", out var t) && t.GetString() is { Length: > 0 } typo)
return $"⚠️ Did you mean {typo}?";
if (v.GetProperty("risk_score").GetInt32() >= 70) return "❌ High risk score.";
return "✅ Registration successful!";
}
foreach (var email in new[] { "alice@mailinator.com", "info@company.com", "alice@stripe.com" })
Console.WriteLine($"{email}: {await Register(email)}");
```
```rust
use reqwest::Client;
use serde_json::Value;
const API_KEY: &str = "YOUR_API_KEY";
async fn validate(client: &Client, email: &str) -> Value {
client.get("https://api.veille.io/v1/email")
.header("x-api-key", API_KEY)
.query(&[("email", email)])
.send().await.unwrap().json::().await.unwrap()
}
async fn register(client: &Client, email: &str) -> &'static str {
let v = validate(client, email).await;
if !v["is_valid"].as_bool().unwrap_or(false) { return "❌ Invalid email."; }
if v["is_disposable"].as_bool().unwrap_or(false) { return "❌ Disposable not accepted."; }
if v["risk_score"].as_i64().unwrap_or(0) >= 70 { return "❌ High risk score."; }
"✅ Registration successful!"
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
for email in ["alice@mailinator.com", "info@company.com", "alice@stripe.com"] {
println!("{}: {}", email, register(&client, email).await);
}
Ok(())
}
```
Role accounts (`admin@`, `info@`, `support@`) are often monitored by multiple people — or not monitored at all. Flag them for manual review rather than blocking outright to avoid losing legitimate B2B signups.
# Validate EU VAT Numbers (/playbooks/veille-vat-validation)
## Overview [#overview]
EU businesses are exempt from VAT when purchasing from other EU businesses — but only with a valid VAT number. This playbook shows how to validate a VAT number at checkout and retrieve the associated company details (name and address) from the VIES database.
## Prerequisites [#prerequisites]
* A Veille API key — get one at [app.veille.io](https://app.veille.io)
* Install dependencies for your language:
```bash
pip install requests
```
No extra dependencies — uses the native `fetch` API (Node 18+).
`curl` extension enabled (on by default).
No extra dependencies — uses `net/http` (Go 1.18+).
No extra dependencies — uses `java.net.http` (Java 11+).
No extra dependencies — uses `System.Net.Http` (.NET 6+).
```toml
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
## Steps [#steps]
### Validate a VAT number [#validate-a-vat-number]
Call `GET /v1/vat` with the `vat` parameter. The number should include the country prefix (e.g. `FR12345678901`).
```python
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.veille.io/v1/vat",
headers={"x-api-key": API_KEY},
params={"vat": "FR12345678901"},
)
result = response.json()
print(result)
```
```typescript
const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ vat: "FR12345678901" });
const response = await fetch(`https://api.veille.io/v1/vat?${params}`, {
headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
```
```php
"FR12345678901"]);
$ch = curl_init("https://api.veille.io/v1/vat?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat=FR12345678901", nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
fmt.Println(result)
}
```
```java
import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/vat?vat=FR12345678901"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```
```csharp
using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync("https://api.veille.io/v1/vat?vat=FR12345678901");
Console.WriteLine(body);
```
```rust
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let result = reqwest::Client::new()
.get("https://api.veille.io/v1/vat")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("vat", "FR12345678901")])
.send().await?.json::().await?;
println!("{:#?}", result);
Ok(())
}
```
### Read the company details [#read-the-company-details]
A valid number returns `is_valid: true`, the `company_name`, `company_address`, and `country_code`.
```python
if result.get("is_valid"):
print(f"✅ Valid VAT number")
print(f" Company : {result.get('company_name')}")
print(f" Address : {result.get('company_address')}")
print(f" Country : {result.get('country_code')}")
else:
print(f"❌ Invalid VAT number: {result.get('error', 'Not found in VIES')}")
```
```typescript
if (result.is_valid) {
console.log("✅ Valid VAT number");
console.log(` Company : ${result.company_name}`);
console.log(` Address : ${result.company_address}`);
console.log(` Country : ${result.country_code}`);
} else {
console.log(`❌ Invalid VAT number: ${result.error ?? "Not found in VIES"}`);
}
```
```php
if ($result["is_valid"] ?? false) {
echo "✅ Valid VAT number\n";
echo " Company : {$result['company_name']}\n";
echo " Address : {$result['company_address']}\n";
echo " Country : {$result['country_code']}\n";
} else {
echo "❌ Invalid VAT number: " . ($result["error"] ?? "Not found in VIES") . "\n";
}
```
```go
if result["is_valid"].(bool) {
fmt.Printf("✅ Valid VAT number\n Company : %v\n Address : %v\n Country : %v\n",
result["company_name"], result["company_address"], result["country_code"])
} else {
errMsg := "Not found in VIES"
if e, ok := result["error"].(string); ok { errMsg = e }
fmt.Println("❌ Invalid VAT number:", errMsg)
}
```
```java
import org.json.*;
var r = new JSONObject(response.body());
if (r.getBoolean("is_valid")) {
System.out.printf("✅ Valid VAT number%n Company : %s%n Address : %s%n Country : %s%n",
r.getString("company_name"), r.getString("company_address"), r.getString("country_code"));
} else {
System.out.println("❌ Invalid VAT number: " + r.optString("error", "Not found in VIES"));
}
```
```csharp
using System.Text.Json;
var r = JsonDocument.Parse(body).RootElement;
if (r.GetProperty("is_valid").GetBoolean())
{
Console.WriteLine("✅ Valid VAT number");
Console.WriteLine($" Company : {r.GetProperty("company_name")}");
Console.WriteLine($" Address : {r.GetProperty("company_address")}");
Console.WriteLine($" Country : {r.GetProperty("country_code")}");
}
else
{
var error = r.TryGetProperty("error", out var e) ? e.GetString() : "Not found in VIES";
Console.WriteLine($"❌ Invalid VAT number: {error}");
}
```
```rust
if result["is_valid"].as_bool().unwrap_or(false) {
println!("✅ Valid VAT number");
println!(" Company : {}", result["company_name"].as_str().unwrap_or(""));
println!(" Address : {}", result["company_address"].as_str().unwrap_or(""));
println!(" Country : {}", result["country_code"].as_str().unwrap_or(""));
} else {
let err = result["error"].as_str().unwrap_or("Not found in VIES");
println!("❌ Invalid VAT number: {}", err);
}
```
### Build a B2B checkout VAT handler [#build-a-b2b-checkout-vat-handler]
Strip VAT from the order total when a verified EU business VAT number is provided.
```python
import requests
API_KEY = "YOUR_API_KEY"
VAT_RATE = 0.20 # 20% VAT
def lookup_vat(number: str) -> dict:
r = requests.get("https://api.veille.io/v1/vat",
headers={"x-api-key": API_KEY}, params={"vat": number})
return r.json()
def calculate_checkout(subtotal: float, vat_number: str | None = None) -> dict:
vat_amount = subtotal * VAT_RATE
vat_info = None
if vat_number:
result = lookup_vat(vat_number)
if result.get("is_valid"):
vat_amount = 0 # B2B reverse charge — no VAT charged
vat_info = {"company": result["company_name"], "country": result["country_code"]}
else:
return {"error": "The VAT number you provided is not valid."}
total = subtotal + vat_amount
return {"subtotal": subtotal, "vat": vat_amount, "total": total, "company": vat_info}
print(calculate_checkout(100.00))
print(calculate_checkout(100.00, vat_number="FR12345678901"))
print(calculate_checkout(100.00, vat_number="INVALID123"))
```
```typescript
const API_KEY = "YOUR_API_KEY";
const VAT_RATE = 0.20;
async function lookupVat(number: string) {
const params = new URLSearchParams({ vat: number });
const res = await fetch(`https://api.veille.io/v1/vat?${params}`, {
headers: { "x-api-key": API_KEY },
});
return res.json();
}
async function calculateCheckout(subtotal: number, vatNumber?: string) {
let vatAmount = subtotal * VAT_RATE;
let companyInfo: any = null;
if (vatNumber) {
const result = await lookupVat(vatNumber);
if (result.is_valid) {
vatAmount = 0;
companyInfo = { company: result.company_name, country: result.country_code };
} else {
return { error: "The VAT number you provided is not valid." };
}
}
return { subtotal, vat: vatAmount, total: subtotal + vatAmount, company: companyInfo };
}
console.log(await calculateCheckout(100));
console.log(await calculateCheckout(100, "FR12345678901"));
console.log(await calculateCheckout(100, "INVALID123"));
```
```php
$number]);
$ch = curl_init("https://api.veille.io/v1/vat?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
return $result;
}
function calculateCheckout(string $apiKey, float $subtotal, ?string $vatNumber = null): array {
$vatAmount = $subtotal * VAT_RATE;
$company = null;
if ($vatNumber) {
$result = lookupVat($apiKey, $vatNumber);
if ($result["is_valid"] ?? false) {
$vatAmount = 0;
$company = ["company" => $result["company_name"], "country" => $result["country_code"]];
} else {
return ["error" => "Invalid VAT number."];
}
}
return ["subtotal" => $subtotal, "vat" => $vatAmount, "total" => $subtotal + $vatAmount, "company" => $company];
}
print_r(calculateCheckout("YOUR_API_KEY", 100.0));
print_r(calculateCheckout("YOUR_API_KEY", 100.0, "FR12345678901"));
```
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
const APIKey = "YOUR_API_KEY"
const VATRate = 0.20
func lookupVAT(number string) map[string]any {
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat="+number, nil)
req.Header.Set("x-api-key", APIKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var r map[string]any
json.Unmarshal(body, &r)
return r
}
func calculateCheckout(subtotal float64, vatNumber string) map[string]any {
vatAmount := subtotal * VATRate
var company map[string]any
if vatNumber != "" {
result := lookupVAT(vatNumber)
if result["is_valid"].(bool) {
vatAmount = 0
company = map[string]any{"company": result["company_name"], "country": result["country_code"]}
} else {
return map[string]any{"error": "Invalid VAT number."}
}
}
return map[string]any{"subtotal": subtotal, "vat": vatAmount, "total": subtotal + vatAmount, "company": company}
}
func main() {
fmt.Println(calculateCheckout(100, ""))
fmt.Println(calculateCheckout(100, "FR12345678901"))
fmt.Println(calculateCheckout(100, "INVALID123"))
}
```
```java
import java.net.URI;
import java.net.http.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
static double VAT_RATE = 0.20;
static JSONObject lookupVat(String number) throws Exception {
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/vat?vat=" + number))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
return new JSONObject(resp.body());
}
static JSONObject calculateCheckout(double subtotal, String vatNumber) throws Exception {
double vatAmount = subtotal * VAT_RATE;
JSONObject company = null;
if (vatNumber != null && !vatNumber.isEmpty()) {
var result = lookupVat(vatNumber);
if (result.getBoolean("is_valid")) {
vatAmount = 0;
company = new JSONObject().put("company", result.getString("company_name"))
.put("country", result.getString("country_code"));
} else {
return new JSONObject().put("error", "Invalid VAT number.");
}
}
return new JSONObject().put("subtotal", subtotal).put("vat", vatAmount)
.put("total", subtotal + vatAmount).put("company", company);
}
public static void main(String[] args) throws Exception {
System.out.println(calculateCheckout(100, null));
System.out.println(calculateCheckout(100, "FR12345678901"));
System.out.println(calculateCheckout(100, "INVALID123"));
}
}
```
```csharp
using System.Net.Http;
using System.Text.Json;
const double VAT_RATE = 0.20;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
async Task LookupVat(string number)
{
var body = await client.GetStringAsync($"https://api.veille.io/v1/vat?vat={number}");
return JsonDocument.Parse(body).RootElement;
}
async Task CalculateCheckout(double subtotal, string? vatNumber = null)
{
double vatAmount = subtotal * VAT_RATE;
string? company = null;
if (vatNumber != null)
{
var result = await LookupVat(vatNumber);
if (result.GetProperty("is_valid").GetBoolean())
{ vatAmount = 0; company = result.GetProperty("company_name").GetString(); }
else return """{"error":"Invalid VAT number."}""";
}
return $"""{{ "subtotal": {subtotal}, "vat": {vatAmount}, "total": {subtotal + vatAmount}, "company": "{company}" }}""";
}
Console.WriteLine(await CalculateCheckout(100));
Console.WriteLine(await CalculateCheckout(100, "FR12345678901"));
Console.WriteLine(await CalculateCheckout(100, "INVALID123"));
```
```rust
use reqwest::Client;
use serde_json::{json, Value};
const VAT_RATE: f64 = 0.20;
const API_KEY: &str = "YOUR_API_KEY";
async fn lookup_vat(client: &Client, number: &str) -> Value {
client.get("https://api.veille.io/v1/vat")
.header("x-api-key", API_KEY)
.query(&[("vat", number)])
.send().await.unwrap().json::().await.unwrap()
}
async fn calculate_checkout(client: &Client, subtotal: f64, vat_number: Option<&str>) -> Value {
let mut vat_amount = subtotal * VAT_RATE;
let mut company = json!(null);
if let Some(number) = vat_number {
let result = lookup_vat(client, number).await;
if result["is_valid"].as_bool().unwrap_or(false) {
vat_amount = 0.0;
company = json!({"company": result["company_name"], "country": result["country_code"]});
} else {
return json!({ "error": "Invalid VAT number." });
}
}
json!({ "subtotal": subtotal, "vat": vat_amount, "total": subtotal + vat_amount, "company": company })
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
println!("{}", calculate_checkout(&client, 100.0, None).await);
println!("{}", calculate_checkout(&client, 100.0, Some("FR12345678901")).await);
println!("{}", calculate_checkout(&client, 100.0, Some("INVALID123")).await);
Ok(())
}
```
Always store the validated company name and address alongside the order record. EU tax authorities require proof that the reverse-charge mechanism was correctly applied.
# Auto top-up (/billing/auto-top-up)
Auto top-up watches your remaining balance and buys a pack for you when it drops below a threshold you choose. It keeps production running when consumption spikes overnight.
### Open subscription settings [#open-subscription-settings]
Go to **Settings → Subscription** and find the **Auto top-up** section.
### Choose a pack and a threshold [#choose-a-pack-and-a-threshold]
Pick the **one-time pack** to buy automatically, then the percentage of remaining balance that triggers it — between 10% and 30%.
### Save [#save]
Enabling auto top-up does not charge you. The first charge only happens when your balance actually crosses the threshold.
## How the threshold is measured [#how-the-threshold-is-measured]
The percentage is calculated on your **combined** remaining balance (API quota plus extra credits), not on a single bucket. When that percentage drops to or below the threshold you set, the configured one-time pack is purchased automatically.
## Built-in protections [#built-in-protections]
| Protection | Effect |
| --------------------------- | --------------------------------------------------------------------------------- |
| **Threshold only** | A purchase fires on balance, never on a schedule or a fixed date. |
| **72-hour cooldown** | After a top-up, no other automatic purchase can fire for 72 hours. |
| **Auto-disable on failure** | If the payment fails, auto top-up switches off so you are not charged repeatedly. |
| **Paid plan required** | Free-trial workspaces cannot enable auto top-up — subscribe first. |
Auto top-up requires a paid plan and a valid payment method. If a charge fails, the feature is disabled and you have to re-enable it manually after updating your card.
## Related [#related]
# Credits and balances (/billing/credits)
Every billable call spends credits from your workspace. The dashboard **Usage** panel under **Settings → Subscription** shows three balances. The API usage endpoint reports a combined runway so you can alert from code without parsing every bucket.
## The three balances [#the-three-balances]
| Balance | What it is | Resets |
| ------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Subscription quota** | API calls included in your plan | Yes — at the start of each billing period (from Stripe when available, otherwise the calendar month). Unused quota does not roll over. |
| **One-time API pack** | Additional API quota from a one-time purchase | Never — consumed until depleted, across billing periods |
| **Pay-as-you-go credits** | A separate ledger used only after subscription quota and API packs are empty | Never — grants stay until spent |
In the dashboard those three bars appear under **Settings → Subscription → Usage** (and in the sidebar **API usage** panel). On the usage endpoint (`/v2/usage`), the `subscription` object is the **combined** API quota (subscription + remaining packs). Pay-as-you-go sits under `credits`. Top-level `remaining` is both added together.
## Consumption order [#consumption-order]
### Subscription quota first [#subscription-quota-first]
Billable calls fill the plan allowance for the current period.
### Then one-time API packs [#then-one-time-api-packs]
When the period allowance is used up, remaining pack quota is consumed next. Packs are attributed in purchase order.
### Then pay-as-you-go credits [#then-pay-as-you-go-credits]
Only if the combined API quota cannot cover the endpoint cost do we debit the pay-as-you-go ledger.
### Otherwise the call is refused [#otherwise-the-call-is-refused]
The API returns `402` with `Payment required`, and the request is not executed.
## What gets billed [#what-gets-billed]
| Outcome | Credits |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `200` (sync success) or `201` (async job completed, in request logs) | Yes — the endpoint's credit cost |
| `202` (async accepted) | No — billed when the job completes (`201` in your request logs) |
| `404` | Usually yes — unless that endpoint marks 404 as free (shown in the Library and on the endpoint page) |
| `401`, `402`, `400`, `5xx`, and most other errors | No — and any pay-as-you-go hold is released |
| Per-row endpoints with **0** results | Free |
| Per-row endpoints with **N** results | N × the per-row cost — all or nothing (insufficient balance → `402` before any rows are returned) |
| `/v2/usage` and job status URLs | Always free — no quota check |
Each endpoint declares its cost in the **Library** (and in the **API reference**): **N credits per call** or **per row**. The balance is checked before the work starts, so a job is never accepted without the credits to pay for it. Polling a job status never costs credits.
## Buy more capacity [#buy-more-capacity]
### Open subscription settings [#open-subscription-settings]
Go to **Settings → Subscription** and click **View all plans** (or open [https://app.piloterr.com/pricing](https://app.piloterr.com/pricing)).
### Pick a one-time pack [#pick-a-one-time-pack]
Depending on the product, a pack adds to **API quota** (one-time API pack) or to the **pay-as-you-go** ledger. It never changes your subscription tier by itself. Larger packs usually cost less per credit.
### Confirm the payment [#confirm-the-payment]
Capacity is available as soon as payment succeeds, and an invoice is emailed to the billing address.
Purchased packs also appear under **Settings → Subscription → One-time packs**. To refill automatically when the combined balance drops, see [Auto top-up](/billing/auto-top-up).
## Where to read history [#where-to-read-history]
| Screen | What it shows |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Settings → Subscription → Usage** | Live remaining / used for the three balances in the current period |
| **API Logs** | Every request with status, duration, and credits charged — including subscription and pack usage |
| **Settings → Credits** | The **pay-as-you-go ledger only**: grants (rewards, some pack types, auto top-up) and spends that happened after API quota was empty. Filter by member or type. |
| **Usage endpoint** (`/v2/usage`) | Machine-readable snapshot: `remaining`, `subscription`, `credits`, `renewal_date` — free to call |
If **Settings → Credits** looks empty while Analytics shows traffic, that is expected: subscription and pack consumption never appear on that page. Use **API Logs** or the Usage bars instead.
## When you see `402` [#when-you-see-402]
| Message | Meaning | What to do |
| ----------------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| `Payment required` | No subscription quota, packs, or pay-as-you-go left for this call's cost | Buy a pack, upgrade the plan, or enable [auto top-up](/billing/auto-top-up) |
| `You have open invoices: …` | An unpaid invoice is blocking the account | Settle it under **Settings → Invoices** — balance alone is not enough |
| `Insufficient credits: X required, Y available` | A per-row endpoint would return more rows than you can afford | Top up, or narrow the request so fewer rows come back |
## FAQ [#faq]
* **Does unused plan quota roll over?** No. The subscription bucket resets each billing period.
* **Do one-time packs expire at month end?** No. They stay until consumed.
* **Do all keys share the same balances?** Yes — quota is workspace-wide.
* **Does checking usage cost credits?** No. `/v2/usage` and job status paths are free.
* **Why is dashboard Usage split but `/usage.subscription` combined?** So a single `remaining` number is enough for alerts; open the dashboard when you need the three-way split.
## Related [#related]
# Invoices and subscription (/billing/invoices-and-subscription)
Your plan sets your API quota for the period and your rate limits. Change it, update your payment details, and download past invoices from the billing pages.
## Change your plan [#change-your-plan]
Open **Settings → Subscription**, click **View all plans**, and pick another plan. Upgrades apply immediately with a prorated charge; downgrades take effect at the end of the current billing period, so you keep the quota you already paid for.
## Payment details and invoices [#payment-details-and-invoices]
* **Open billing portal** from **Settings → Invoices** or **Subscription** to change your card, company name, address, and VAT number. Set them before your first invoice so they appear on the document.
* Every invoice is listed under **Settings → Invoices** with its date, amount, and status, and can be downloaded as a **PDF**.
* Set the **billing email** on the same page to control where invoices are sent — use a finance alias rather than a personal address.
* An **upcoming invoice** may appear when a renewal is about to be charged — useful for forecasting the next bill.
## If an invoice stays unpaid [#if-an-invoice-stays-unpaid]
Open invoices block API traffic with `402`, even when credits remain. Settle them from the billing portal or the Invoices page, then retry. Do not keep retrying a `402` — it will fail again until the invoice is paid or the balance is topped up.
Cancelling stops the renewal but keeps your account and remaining credits. You can subscribe again later without losing your keys or history.
## Related [#related]
# Plans and pricing (/billing/plans-and-pricing)
These are the plans shown on the Piloterr dashboard pricing page ([https://app.piloterr.com/pricing](https://app.piloterr.com/pricing)). Your workspace picks one plan (or a one-time pack where available); the quota and rate limits on that plan apply to every API key in the workspace.
## Plans [#plans]
| Plan | Price | Quota |
| --------------- | ------- | ------------------------ |
| **Free trial** | $0/mo | +50 API quota (one-time) |
| **Premium** | $49/mo | 18,000 credits/month |
| **Premium+** | $99/mo | 40,000 credits/month |
| **Startup** | $249/mo | 110,000 credits/month |
| **Startup+** | $499/mo | 230,000 credits/month |
| **Enterprise** | $799/mo | 390,000 credits/month |
| **Enterprise+** | $999/mo | 530,000 credits/month |
## How a plan applies [#how-a-plan-applies]
* Quota and rate limits are **workspace-wide**: every API key shares the same pool.
* Changing plan does not rotate or invalidate existing keys.
* One-time packs add to API quota without changing the subscription tier.
## Choose a plan [#choose-a-plan]
### Open billing settings [#open-billing-settings]
Go to **Settings → Subscription** and click **View all plans**, or open [https://app.piloterr.com/pricing](https://app.piloterr.com/pricing).
### Pick a plan or pack [#pick-a-plan-or-pack]
Subscription upgrades apply immediately with a prorated charge; downgrades take effect at the end of the current billing period. One-time packs add quota as soon as payment succeeds.
### Need a higher volume? [#need-a-higher-volume]
Pick a higher tier on the dashboard pricing page, or contact support if you need a custom volume.
Not sure which tier fits? Build a scenario in the [Simulator](/usage/simulator) first — it forecasts credits without sending traffic. Prices on this page match [https://app.piloterr.com/pricing](https://app.piloterr.com/pricing); checkout always shows the live amount before you confirm.
## Related [#related]
# Finding your way around (/dashboard/navigation)
Everything in the dashboard hangs off the left sidebar. This page maps each entry to what you can do there, so you can stop hunting for a setting.
## The sidebar [#the-sidebar]
| Where | What you do there |
| ------------------------ | ---------------------------------------------------------------------------- |
| **Library** | Browse endpoints, read their parameters, and run them. |
| **Analytics** | Consumption per endpoint, per key, and over time. |
| **API Logs** | Per-request history with status, duration, and cost. |
| **API Keys** | Create, edit, deactivate, and rotate API keys. |
| **Settings → Account** | **Name**, **Company logo**, country (currency is read-only), and privacy. |
| **Settings → Members** | Invite teammates and manage their roles. |
| **Settings → Simulator** | Estimate the cost of a scenario before you build it. |
| **Settings → Billing** | **Subscription** (plan, auto top-up), **Invoices**, and **Credits** history. |
| **Settings → Support** | **New Ticket**, **My Tickets**, and **API Status**. |
## The API usage panel [#the-api-usage-panel]
At the foot of the sidebar, the **API usage** panel shows your remaining balances (subscription quota, one-time API pack, pay-as-you-go). **Upgrade** opens checkout for the current account. If an invoice is unpaid, a warning appears on the same panel. The combined runway is what the usage endpoint returns as `remaining`.
## The avatar menu [#the-avatar-menu]
Your avatar at the bottom of the sidebar opens, in order: **Profile**, **Subscription**, **Account**, the account audit **Logs** (who changed what — not the same as **API Logs**), **Affiliate**, **Feedback**, then theme and **Sign out**. Extra program links (**API Docs**, **Bug Bounty**, **Startup Program**, **Ambassador**) may also appear.
The account switcher sits at the top of the sidebar. If a key or invoice is missing, you are almost always looking at the wrong account.
## Related [#related]
# Dashboard overview (/dashboard/overview)
Signing in lands you on the **Library**, the catalogue of endpoints you can call. **Analytics** is the screen that tells you the state of your account: how many credits you are burning, how fast the API is answering, and whether recent calls are succeeding.
## What each Analytics panel tells you [#what-each-analytics-panel-tells-you]
| Panel | Read it when |
| ------------------------------------- | ------------------------------------------------------------------- |
| **Credits Usage** | You want to know how much of the period's quota you have consumed. |
| **Total Requests** | You need to compare traffic volume with your credit spend. |
| **Avg. Response Time** | Calls feel slow and you want to confirm it before raising a ticket. |
| **Endpoints Used** | You are checking that a migration actually switched endpoints. |
| **Status codes** and **Success Rate** | Failures may be wasting calls without producing results. |
## The Library [#the-library]
The Library lists every endpoint available on your plan with its credit cost. Open one to read its parameters, run it in the playground, or copy a ready-made code snippet.
## How the pieces fit together [#how-the-pieces-fit-together]
| You want to… | Go to |
| ------------------------------- | ------------------------------------------------ |
| Discover an endpoint and try it | **Library** → open the endpoint → playground |
| See if traffic is healthy today | **Analytics** (period filters at the top) |
| Debug one failed call | **API Logs** → filter → open the row |
| Know how much runway is left | **API usage** panel at the bottom of the sidebar |
| Buy more credits or change plan | **Settings → Subscription** |
Click **View more analytics** for the per-endpoint and per-key breakdown. Use **Customize** on Analytics to show, hide, or reorder the widgets.
## Related [#related]
# Create your account (/getting-started/create-account)
Creating an account takes a minute and gives you a workspace, a starting credit balance, and access to the dashboard.
### Open the signup page [#open-the-signup-page]
Go to [https://app.piloterr.com/register](https://app.piloterr.com/register).
### Choose how to sign up [#choose-how-to-sign-up]
Use your email address and a password, or continue with **Google** or **GitHub** for a one-click signup.
### Verify your email [#verify-your-email]
We send a confirmation link to your inbox. Click it to activate the account. If it does not arrive within a few minutes, check your spam folder and request a new link from the sign-in page.
### Check your workspace name [#check-your-workspace-name]
Your account holds your API keys, credits, and invoices. Under **Settings → Account** you can set the **Name**, **Company logo**, and country. Currency is chosen at signup and is not editable afterwards.
## What you get next [#what-you-get-next]
* An account with a starting credit balance, so you can try the API before picking a plan.
* Access to the **Library**, **API Keys**, **API Logs**, and **Analytics**.
* A seat for yourself as **SuperUser** — invite teammates later under **Settings → Members**.
Use a shared team address (for example `dev@yourcompany.com`) for the first member. It keeps billing emails reachable when someone leaves.
## Related [#related]
# What is Piloterr? (/getting-started/overview)
Piloterr is an HTTP API you call from your own backend, automation platform, or AI agent. You send a request with your API key, we do the work, and you get structured JSON back. Each successful call consumes credits from your account balance.
## The three things you need [#the-three-things-you-need]
| What | Where |
| ---------- | -------------------------------------------------------------------------------- |
| An account | Sign up on the dashboard at [https://app.piloterr.com](https://app.piloterr.com) |
| An API key | **API Keys** in the sidebar, sent as the `x-api-key` header |
| Credits | Included with your plan, topped up manually or automatically |
## What happens on a request [#what-happens-on-a-request]
### Your backend sends the call [#your-backend-sends-the-call]
The request hits `https://api.piloterr.com` with the `x-api-key` header. Never put the key in a browser or mobile app.
### We check the key and the balance [#we-check-the-key-and-the-balance]
Invalid, inactive, or expired keys return `401`. An empty balance or unpaid invoice returns `402`. Rate limits also return `401`, with a message that names the limit.
### We run the endpoint and return JSON [#we-run-the-endpoint-and-return-json]
On success you get structured data. The call is logged under **API Logs** with its status, duration, and credit cost.
### Credits are deducted only when the call succeeds [#credits-are-deducted-only-when-the-call-succeeds]
Successful responses are billed. Authentication failures, bad parameters, and server errors cost nothing — so you can experiment without burning the balance.
## What the dashboard is for [#what-the-dashboard-is-for]
The API itself is stateless: you authenticate with a key and get a response. The dashboard is where you manage everything around it — browsing the endpoint **Library**, issuing and revoking keys, watching consumption, downloading invoices, and inviting teammates. You never need the dashboard to make a call, but you will use it to keep an integration healthy.
After signup you land on the **Library**. The remaining balance also sits at the bottom of the sidebar on every screen, so you always know how much runway you have left.
## Related [#related]
# Quick start (/getting-started/quick-start)
This walkthrough takes you from an empty account to a successful API call. You will create a key, send a request, and confirm it in your logs.
### Create an account [#create-an-account]
Sign up at [https://app.piloterr.com/register](https://app.piloterr.com/register) and verify your email.
### Create an API key [#create-an-api-key]
Open **API Keys** in the sidebar and click **New API Key**. Give it a **Name** such as `local-dev` and pick the `development` category. The secret is shown **once** — copy it immediately.
### Store the key [#store-the-key]
Save the secret in an environment variable rather than in your source code:
```bash
export API_KEY="your-secret-key"
```
### Call the API [#call-the-api]
Send your first request with the key in the `x-api-key` header. The usage endpoint is free, so it is a safe way to verify that authentication works:
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: $API_KEY"
```
### Confirm in the dashboard [#confirm-in-the-dashboard]
Open **API Logs** in the sidebar. Your call appears within a few seconds with its status code, duration, and credit cost.
## Prefer clicking first? [#prefer-clicking-first]
Open any endpoint in the **Library**, fill in the parameters in the **Playground**, and run it. That call uses the same credits and appears in the same logs — language tabs inside the playground (curl, Python, Node…) give you the equivalent snippet.
Never call the API directly from a browser or mobile app. Anyone can read the key from the network tab. Always proxy requests through your own backend.
## Related [#related]
# Sign in (/getting-started/sign-in)
Sign in at [https://app.piloterr.com](https://app.piloterr.com) with the method you used at signup. Email and social logins both lead to the same workspace as long as the address matches.
## Forgot your password [#forgot-your-password]
### Request a reset link [#request-a-reset-link]
Click **Forgot password** on the sign-in page and enter your email address.
### Open the email [#open-the-email]
The reset link is valid for a limited time. Request a new one if it has expired.
### Set a new password [#set-a-new-password]
Choose a new password and sign in. Existing API keys keep working: resetting a password never invalidates a key.
## Where you land [#where-you-land]
After a successful sign-in you open on the **Library** for your default workspace. If you belong to several workspaces, you may see the workspace picker first — pick one, then the same Library view appears.
## Switching workspaces [#switching-workspaces]
Use the switcher at the top of the sidebar to jump between workspaces. Credits, keys, and invoices are scoped per workspace, so make sure the right one is selected before creating a key or reading a balance.
Signed in with Google or GitHub? You have no password to reset. Use the same provider button, or set a password from **Settings → Profile → Security**.
## Related [#related]
# Common issues (/help/common-issues)
Most support tickets come down to a handful of causes. Work through this page first — it usually resolves the issue faster than waiting for a reply.
| Symptom | Most likely cause | Fix |
| ---------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Every call returns `401` | Wrong header name, or a space copied with the secret | The header is `x-api-key`. Re-copy the secret, or create a new key. |
| `401` `Rate limit exceeded` | A per-key quota, or your plan's rate limit | Spread the traffic out, raise the key's limits, or upgrade the plan. |
| Calls suddenly return `402` | Balance exhausted, or an unpaid invoice | Buy a [top-up](/billing/credits), enable [auto top-up](/billing/auto-top-up), or settle the invoice. |
| Timeouts at exactly 10s or 30s | Your platform aborts before we reply | Raise your function or client timeout to 60 seconds. |
| `400` on a request that used to work | A required parameter is missing or mistyped | Compare against the endpoint page in the **API reference**. |
| Credits drop faster than expected | Retries, or a loop calling the same endpoint | Check the [request logs](/usage/request-logs) for repeated identical calls. |
| Balance or keys missing in the dashboard | Wrong workspace selected | Use the switcher at the top of the sidebar. |
| Playground burns credits unexpectedly | Playground calls are real API calls | Use the free usage endpoint to test auth, or the [Simulator](/usage/simulator) to estimate cost without traffic. |
| `402` with credits still showing | An unpaid invoice is blocking the account | Open **Settings → Invoices**, settle the invoice, then retry. |
## A 60-second checklist [#a-60-second-checklist]
### Read the status code [#read-the-status-code]
`401` → key or rate limit. `402` → balance or invoice. `400` → parameters. `5xx` → retry with backoff.
### Find the row in API Logs [#find-the-row-in-api-logs]
Match the timestamp. Open the detail panel for the exact parameters and error message.
### Check the sidebar balance [#check-the-sidebar-balance]
If it is near zero, top up or enable auto top-up before retrying a production load.
## Still stuck [#still-stuck]
Open a [support ticket](/help/support-tickets) from **Settings → Support → New Ticket**. Include the endpoint, the timestamp, and the status code you saw — with those three, we can find the request in seconds. **API Status** on the same menu tells you whether the problem is on our side.
## Related [#related]
# Support tickets (/help/support-tickets)
When [common issues](/help/common-issues) does not unblock you, open a ticket from **Settings → Support**. You can attach logs, track replies, and mark the conversation resolved when you are done.
## Where to find it [#where-to-find-it]
In the dashboard sidebar, open **Settings → Support**. You get three entries: **New Ticket**, **My Tickets**, and **API Status**. Tickets are per workspace — switch account first if you have more than one.
## Open a ticket [#open-a-ticket]
### Choose New Ticket [#choose-new-ticket]
Go to **Settings → Support → New Ticket**. Read the **Support guidelines** banner before you submit.
### Fill in the form [#fill-in-the-form]
Add a short **Title**, set **Priority**, pick a **Category** and **Subcategory**, then describe the issue in the message. Attach a file if it helps.
### Submit [#submit]
Click **Create ticket**. You land on the ticket thread, and email notifications keep you updated when the team replies.
## What to include [#what-to-include]
* The **endpoint** path (for example `/v2/usage` or `/v2/google/search`).
* The **timestamp** of the failing call (UTC if you can).
* The **HTTP status code** and the exact `error` string from the response.
* A `request_id` from [API Logs](/usage/request-logs) when you have one.
* What you expected vs what you got, in one or two sentences.
Support is available **Monday to Friday, 9:00–17:00 (Paris time)**. We answer every ticket; response time depends on your plan and support level. Idle tickets may be closed by the team.
## Categories [#categories]
| Category | Typical subcategories |
| --------------- | ---------------------------------------------------------------- |
| **Billing** | Invoice, Payment failure, Refund or credit, Plan change |
| **Account** | API key, Dashboard login, Rate limit |
| **Technical** | API errors, Timeout / performance, Incorrect data, Endpoint down |
| **Integration** | SDK bug, Auth flow, Webhook |
| **Commercial** | Partnership, Reseller, Pricing, Custom plan, Review voucher |
## Priorities and statuses [#priorities-and-statuses]
| Field | Values |
| ------------ | --------------------------------------------- |
| **Priority** | Low, Medium, High, Urgent |
| **Status** | Open, In progress, Resolved, Closed, Archived |
## Follow the conversation [#follow-the-conversation]
Open any ticket from **My Tickets**. You see the original message, attachments, and the **Messages** thread. Reply with optional attachments while the ticket is open or in progress. When the issue is fixed on your side, click **Mark as resolved**. Replies are disabled once a ticket is **Resolved** or **Closed**.
* The Playground can pre-fill a technical ticket with endpoint and status context — complete the placeholders before you send.
* Check **API Status** on the same Support menu if you suspect a platform-wide incident.
## Limits [#limits]
* **Free trial** accounts can open **one** support ticket in total.
* **Paid** accounts can open up to **10 tickets per day**.
* Attachments accept `.png`, `.jpg`, `.jpeg`, `.txt`, `.log`, and `.json`, up to **10 MB**.
If you already have an active live chat, continue there — do not open a duplicate ticket for the same issue.
## Before you open a ticket [#before-you-open-a-ticket]
Work through [Common issues](/help/common-issues) first. Many `401`, `402`, and timeout cases are fixed from the dashboard without waiting for a reply.
## Related [#related]
# Create and manage API keys (/keys/create-and-manage)
API keys authenticate every request. You manage them under **API Keys** in the sidebar, where each key carries a name, a category, optional sliding-window limits, and an optional expiry date.
## Create a key [#create-a-key]
### Open the form [#open-the-form]
Go to **API Keys** and click **New API Key**.
### Fill in the details [#fill-in-the-details]
Give the key a **Name** you will recognise in logs (`prod-api`, `zapier`, `local-dev`), pick a **Category**, and optionally set an expiry date.
### Set request limits [#set-request-limits]
The **Request limits** tab caps how much a single key can spend. Leave the fields empty for an unlimited key.
### Copy the secret [#copy-the-secret]
The secret is displayed **once**, right after creation. Copy it into your secret manager or environment variables. If you lose it, create a new key — it cannot be recovered.
## Per-key limits [#per-key-limits]
| Limit | Counts |
| -------------------- | --------------------------------------- |
| **Global hard-stop** | Every request the key has ever made. |
| **Within 24h** | The last 24 hours, as a rolling window. |
| **Within 7d** | The last 7 days, as a rolling window. |
| **Within 30d** | The last 30 days, as a rolling window. |
The windows roll continuously rather than resetting at midnight or on the 1st. A key that hits its limit returns `401` with a message naming the quota, while the rest of your keys keep working.
## One key per environment [#one-key-per-environment]
| Environment | Category | Why |
| ----------------- | ------------- | ------------------------------------------- |
| Local development | `development` | Revoke freely without touching production. |
| CI and staging | `staging` | Isolate automated test traffic in the logs. |
| Production | `production` | Rotate on a schedule, watch it closely. |
## Deactivate, delete, or rotate [#deactivate-delete-or-rotate]
* **Deactivate** blocks the key immediately while keeping its history. Use it when a key may have leaked.
* **Delete** is only possible for keys that were never used. Anything with request history is kept for billing and auditing.
* **Rotate** by creating the replacement first, deploying it everywhere, then deactivating the old key. That order avoids downtime.
A leaked key can spend your entire balance. Deactivate it right away, then create a replacement — do not wait for the rotation window.
## Related [#related]
# Make your first call (/keys/first-call)
Once you have a key, authentication is a single header. This page shows the shape of a request, how to confirm it worked, and what to do when it does not.
## Send the request [#send-the-request]
Pass your key in the `x-api-key` header on every call. The usage endpoint is free, which makes it the safest way to verify a new key:
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: $API_KEY"
```
```javascript
const res = await fetch("https://api.piloterr.com/v2/usage", {
headers: { "x-api-key": process.env.API_KEY },
})
if (!res.ok) throw new Error(`Request failed: ${res.status}`)
const usage = await res.json()
```
## What a successful usage response looks like [#what-a-successful-usage-response-looks-like]
A `200` from the usage endpoint means the key works. The JSON includes `remaining` (combined runway), `subscription` and `credits` balances, `renewal_date` when a period applies, and the `api_key` that made the call — including its quotas. None of that call costs credits.
## If it does not work [#if-it-does-not-work]
| Response | Cause | Fix |
| --------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `401 Invalid API Key` | The header is missing or the value is wrong | Check for a trailing space or newline in the copied secret |
| `401 Inactive API Key` | The key was deactivated | Reactivate it under **API Keys**, or create a new one |
| `401` `Rate limit exceeded` | A per-key quota or your plan's rate limit was hit | Slow down, or raise the limits on the key |
| `402` `Payment required` | No credits left, or an unpaid invoice | Add a [top-up](/billing/credits), enable [auto top-up](/billing/auto-top-up), or settle the invoice |
Set your HTTP client timeout to at least 60 seconds. Many platform defaults are far lower and abort the call before Piloterr has replied.
## Related [#related]
# Profile and account settings (/account/profile-and-settings)
Two screens, two scopes: **Profile** holds what is personal to you and follows you across accounts, while **Account** holds settings that affect every member of the selected account.
## Which setting lives where [#which-setting-lives-where]
| Setting | Where |
| --------------------------------------------------------------- | -------------------------------------- |
| Your name, avatar, and interface language | **Settings → Profile** |
| Your password | **Settings → Profile → Security** |
| Product, usage, and marketing emails for you | **Settings → Profile → Notifications** |
| **Name**, **Company logo**, and country (currency is read-only) | **Settings → Account** |
| Weekly and monthly usage reports for the account | **Settings → Account** |
| Log privacy and account deletion | **Settings → Account** |
## Notifications [#notifications]
Email preferences on **Settings → Profile → Notifications** follow you across every account you belong to. Each category is independent, except **Opt out of all emails**, which turns the three others off.
| Preference | What it is for |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Product updates** | News about the platform: new APIs in the Library and product improvements. Typical email: a digest of recently added endpoints. |
| **Usage updates** | Operational alerts about your consumption: credits running low, a key that hit its rate limit, a limited subscription, or an expired key. |
| **Marketing emails** | Onboarding, offers, and commercial follow-ups: welcome and coupon after signup, a nudge if you have not made a first request, a first-request recap, checkout reminders, and a follow-up after a subscription cancel. |
| **Opt out of all emails** | Stops product, usage, and marketing emails. Transactional mail (password reset, email verification, invitations, invoices, ticket replies) still arrives. |
Weekly and monthly **account** usage summaries are a second layer. The workspace toggle lives under **Settings → Account**. Even when that toggle is on, you only receive the report if **Usage updates** is enabled on your profile.
| Kind | Where it is controlled |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Product updates, usage alerts, and marketing emails | **Settings → Profile → Notifications** (follows you across accounts) |
| Weekly / monthly **account** usage reports | **Settings → Account → Notifications** (shared with every member), plus your personal **Usage updates** preference |
Every non-essential email includes an **Unsubscribe** link. It opens the same preferences as **Settings → Profile → Notifications**, without signing in.
## Log privacy [#log-privacy]
Turning on log privacy stops the dashboard from storing the parameters sent with each request. Status codes, durations, and credit costs are still recorded, so billing and monitoring keep working — but you lose the request payload when debugging.
## Danger zone [#danger-zone]
* **Delete your user** from **Settings → Profile** removes only you. Account data stays for the remaining members.
* **Delete the account** from **Settings → Account** is irreversible: keys, logs, and credits for that account are gone. You must type the account slug to confirm.
Your sign-in email is read-only in the dashboard. Open a [support ticket](/help/support-tickets) from **Settings → Support** if you need it changed.
## Related [#related]
# Monitor your usage (/usage/monitor-usage)
**Analytics** breaks down where your credits go. Use it to find the endpoint driving your bill, confirm a migration, or catch a client that started retrying in a loop.
## Filter first [#filter-first]
Pick a period (7, 30, or 90 days, or a custom range), then narrow by endpoint or by status code family. **View more analytics** opens the detailed screen, where you can also filter by API key and dig into recent logs without leaving Analytics.
## What to look at [#what-to-look-at]
| View | Answers |
| --------------------- | ----------------------------------------------------- |
| **Top endpoints** | Which calls cost the most credits? |
| **API key analytics** | Which environment or client is responsible? |
| **Traffic over time** | Is consumption stable, or did something change? |
| **Status codes** | Are failures wasting calls without producing results? |
## When numbers look wrong [#when-numbers-look-wrong]
* Confirm the **workspace** in the sidebar switcher — balances never mix across workspaces.
* Confirm the **period** filter. A quiet 7-day view can hide a spike from last month.
* Compare **credits usage** with **total requests**. If requests are high but credits are not, you may be hitting free endpoints or getting a lot of errors.
## Check your balance from code [#check-your-balance-from-code]
The usage endpoint returns the same numbers as the dashboard and is free to call. Query it on a schedule and alert your team before the balance runs out:
```bash
curl "https://api.piloterr.com/v2/usage" \
-H "x-api-key: $API_KEY"
```
## Platform health with API Stats [#platform-health-with-api-stats]
The [Stats](/stats) endpoint returns infrastructure health for an engine or a path over the last hour. It is not your account usage. The call is free and never consumes credits.
API Stats is enabled per account. HTTP 403 means it is not active on your account. Open a [support ticket](/help/support-tickets) and ask us to activate it.
Analytics is aggregated and can lag by a few minutes. For request-level accuracy, use the [request logs](/usage/request-logs).
## Related [#related]
# Request logs (/usage/request-logs)
Every call is recorded under **API Logs**, with the endpoint, status code, duration, caller IP, and credits charged. This is where you go when a request behaved unexpectedly.
## Debugging a failed call [#debugging-a-failed-call]
### Find the request [#find-the-request]
Filter by date range, API key, endpoint, status, or IP — or paste a request ID straight into the search box.
### Open the detail view [#open-the-detail-view]
Click the row to open a panel with the parameters that were sent and the response that was returned.
### Match it to a status code [#match-it-to-a-status-code]
`400` means a parameter is wrong, `401` points at the key or a rate limit, `402` means an empty balance, `403` on [Stats](/stats) means API Stats is not enabled on your account, and `500` is on our side and worth retrying.
## Make the screen yours [#make-the-screen-yours]
* Toggle columns to show what matters to you: credits, speed, IP, parameters, or the error message.
* Switch to the **time series** view to see the same filtered traffic as a chart.
* Save a set of filters as a view so you can come back to it in one click.
* Turn on auto-refresh when you are watching a live incident.
## What is stored [#what-is-stored]
By default each row keeps the parameters that were sent and a truncated response body. If your workspace turns on **log privacy** under **Settings → Account**, parameters stop being stored — status, duration, and credit cost remain, so billing still works.
If your client reports a timeout but the log shows a `200`, the call succeeded and your own runtime closed the connection early. Raise your HTTP timeout to 60 seconds.
## Related [#related]
# Test endpoints and estimate cost (/usage/simulator)
The dashboard gives you two ways to try things before you commit: the **playground** on each Library endpoint sends real requests with your key attached, and the **Simulator** forecasts what a workload would cost without calling anything.
## Run a request in the playground [#run-a-request-in-the-playground]
### Open an endpoint [#open-an-endpoint]
Go to **Library** and open the endpoint you want to try.
### Fill in the parameters [#fill-in-the-parameters]
Required fields are marked. Optional ones fall back to their documented defaults.
### Run it and read the response [#run-it-and-read-the-response]
You get the status code and the response body on the **Response** tab. Language tabs inside the **Playground** (curl, Python, Node…) copy the same call as a snippet.
Playground calls are real API calls: they consume credits and appear in your logs exactly like production traffic.
## Estimate a workload with the Simulator [#estimate-a-workload-with-the-simulator]
**Settings → Simulator** answers the other question: what would this cost? Add the endpoints you plan to call, set a number of requests for each, and pick a plan. You get the total requests, the credits needed, and any excess beyond the plan — then you can email the estimate to yourself or to whoever signs off on it.
The Simulator sends no traffic and consumes no credits, so it is the safe way to size a plan before a launch.
## Related [#related]
# Invite your team (/team/members-and-groups)
A workspace can hold several members, so keys, credits, and invoices are shared instead of tied to one personal account. Manage members under **Settings → Members**.
### Send an invitation [#send-an-invitation]
Go to **Settings → Members**, click **Invite member**, and enter your teammate's email address and name.
### Pick a role [#pick-a-role]
Roles control who can create keys, change the plan, and see invoices. Give billing access only to the people who need it.
### Follow the invitation [#follow-the-invitation]
Pending invitations stay listed on the same page until they are accepted, so you can chase or cancel them.
## Roles [#roles]
| Role | Typical use |
| ----------------- | ------------------------------------------------------- |
| **SuperUser** | Full control, including deleting the account. |
| **Admin** | Day-to-day administration: keys, members, and settings. |
| **Billing Admin** | Plan, invoices, and payment details. |
| **User** | Everyday usage without administrative changes. |
Not sure which one to pick? **View all roles and permissions** on the Members page shows the full matrix, permission by permission.
## Groups [#groups]
When **Groups** is enabled on your plan, the Members page also lets you organise people into groups for easier access management. If you do not see the tab, your plan does not include it yet.
## How invitations work [#how-invitations-work]
* The invitee receives an email with a link to `/invitation/...`. They can create an account or sign in if they already have one.
* Until they accept, the invitation stays under **Pending invitations** so you can resend or cancel it.
* Once accepted, they share the account's keys, credits, and invoices according to their role — they do not get a separate balance.
Removing a member does not revoke the API keys they created. Deactivate their keys under **API Keys** as part of offboarding.
## Related [#related]