Scrappa's GET /api/otto/search returns OTTO (otto.de) products for a search query as structured JSON. A page holds up to 120 products.
What you get
Each product has id, variation_id, article_number, title, brand, category, url, price, original_price, discount_percent, currency, on_sale, price_from, rating, review_count, availability, multiple_sizes, colors and images. Use id with the product and reviews endpoints. total is the number of matches OTTO reports.
Prices
Prices are numbers in currency (EUR). price is the current price. For reduced products original_price is the list price (UVP) and discount_percent the saving. price_from is true when OTTO shows an "ab" price for a product with several variations.
Filters are honoured
brand, color, min_price, max_price and on_sale are part of the search itself. on_sale=true also returns products with an extra discount (extra_discount, for example "-20% Extra"). min_price and max_price are whole euros: min_price=50&max_price=100 returns prices from 50,00 to 100,00 (both ends included). Every returned price is checked, and a product that breaks a filter is removed. filtered_out counts them. If no product matches, items is empty and total is 0. OTTO's "similar products" fallback is never returned.
Use the brand as search results show it (for example Marikoo or Levi's). color is one of schwarz, weiss, grau, blau, rot, gruen, gelb, braun, beige, rosa, lila, orange, bunt.
Sort and pages
sort is relevance (default), rating, price_low_to_high or price_high_to_low. page is 1-100. A page past the last result returns an empty items list, never page 1. Umlauts in query are handled for you.
Credits
A successful search costs 1 credit. If the usual route fails and the search has to use a more expensive route, it costs 1 credits instead. Errors, blocks and 404 answers cost nothing. The response shows the charge in meta.credits_charged.
Run this endpoint
Endpoint
Parameters
Start with the required fields, then add optional filters only when your use case needs them.
Runnable path
1 required parameter needed before sending a request.
7 optional filters available.
string
Required
Search words, for example winterjacke damen.
winterjacke damen
string
Optional
Brand as OTTO shows it, for example Marikoo. Only this brand is returned.
example
string
Optional
Base colour: schwarz, weiss, grau, blau, rot, gruen, gelb, braun, beige, rosa, lila, orange, bunt.
example
integer
Optional
Lowest price in whole euros.
10
integer
Optional
Highest price in whole euros. Must not be lower than min_price.
100
boolean
Optional
true for reduced products only.
true
string
Optional
relevance (default), rating, price_low_to_high or price_high_to_low.
relevance
integer
Optional
Result page of 120 products, 1-100.
1
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: YOUR_API_KEY_HERE"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
<?php
use Illuminate\Support\Facades\Http;
$response = Http::timeout(30)
->withHeaders(['x-api-key' => 'YOUR_API_KEY_HERE'])
->get('https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true');
if ($response->successful()) {
echo $response->body();
} else {
echo "Error: " . $response->status();
}
const options = {
method: 'GET',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE'
}
};
fetch('https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true', options)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
const axios = require('axios');
const options = {
method: 'GET',
url: 'https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true',
headers: {
x-api-key: 'YOUR_API_KEY_HERE',
}
};
try {
const response = await axios(options);
console.log(response.data);
} catch (error) {
console.error('Error:', error.message);
}
require 'net/http'
require 'uri'
uri = URI.parse("https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri.request_uri)
request['x-api-key'] = 'YOUR_API_KEY_HERE'
begin
response = http.request(request)
puts response.body
rescue => e
puts "Error: #{e.message}"
end
import http.client
import json
conn = http.client.HTTPSConnection("scrappa.co")
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
conn.request("GET", "/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
except Exception as e:
print(f"Error: {e}")
finally:
conn.close()
import requests
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
response = requests.get('https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true', headers=headers)
response.raise_for_status()
print(response.text)
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApiExample {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true")
.addHeader("x-api-key", "YOUR_API_KEY_HERE")
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
System.out.println("Error: " + response.code());
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("x-api-key", "YOUR_API_KEY_HERE")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println(string(body))
}
#!/bin/bash
curl -X GET \
-H "x-api-key: YOUR_API_KEY_HERE" \
"https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true"
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");
try
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true"));
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
import axios from 'axios';
async function run(): Promise<void> {
try {
const response = await axios({
method: 'GET',
url: 'https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE',
},
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}
void run();
use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://scrappa.co/api/otto/search?query=winterjacke+damen&max_price=100&on_sale=true")
.header("x-api-key", "YOUR_API_KEY_HERE")
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
Response Schema
Example response fields are illustrative; inspect the JSON before integrating.
Example response fields
Scan these fields before integrating.
success
query
items
total
page
page_size
has_more
sort
+3 more
Common items fields
position
id
variation_id
article_number
{
"success": true,
"query": "winterjacke damen",
"items": [
{
"position": 1,
"id": "C774733826",
"variation_id": "1172134399",
"article_number": "50293859",
"title": "Marikoo Outdoorjacke Erdbeere sportliche Funktionsjacke mit Kapuze",
"brand": "Marikoo",
"category": "Jacken",
"url": "https://www.otto.de/p/marikoo-outdoorjacke-erdbeere-sportliche-funktionsjacke-mit-kapuze-C774733826/?variationId=1172134399",
"price": 79.9,
"original_price": 119.9,
"discount_percent": 33,
"currency": "EUR",
"price_from": false,
"on_sale": true,
"extra_discount": null,
"rating": 4.5,
"review_count": 773,
"availability": {
"state": "AVAILABLE",
"text": "lieferbar - in 3-4 Werktagen bei dir"
},
"multiple_sizes": true,
"colors": [
{
"name": "weinrot",
"base_color": "rot",
"hex": "#60303B",
"variation_id": "1172134399",
"selected": true
}
],
"images": [
"https://i.otto.de/i/otto/f0f6fe49-54fc-5a0d-a2a2-800c86483c4f?$formatz$"
]
}
],
"total": 1985,
"page": 1,
"page_size": 120,
"has_more": true,
"sort": "relevance",
"applied_filters": {
"max_price": 100,
"on_sale": true
},
"filtered_out": 0,
"meta": {
"endpoint_family": "otto-search",
"billable": true,
"attempts": 2,
"credits_charged": 1
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
Filter Not Applied
OTTO rejected the search rule built from your filters (`upstream_rejected`). The request fails instead of returning unfiltered products. Non-billable.
{
"meta": {
"attempts": 1,
"billable": false,
"retryable": false,
"endpoint_family": "otto-search"
},
"error": {
"code": "upstream_rejected",
"message": "OTTO did not apply a requested filter. The request is rejected instead of returning unfiltered results."
},
"success": false
}Validation Error
A query parameter is missing, unknown or unusable. Unknown parameters are rejected, never ignored. Non-billable.
{
"errors": {
"sort": [
"The selected sort is invalid."
]
},
"message": "The selected sort is invalid."
}Upstream Unavailable
OTTO could not be reached in time or answered with a block or an unexpected page (`upstream_unavailable`, `upstream_blocked`, `parser_drift`). Retryable and never billed. Clients are never rate limited.
{
"meta": {
"attempts": 2,
"billable": false,
"retryable": true,
"endpoint_family": "otto-search"
},
"error": {
"code": "upstream_unavailable",
"message": "OTTO is temporarily unavailable. Please retry."
},
"success": false
}Generate Code with AI
Copy a ready-made prompt with all the endpoint details, parameters, and example responses. Paste it into ChatGPT, Claude, or any AI assistant to instantly generate working code.
Try It Live
Test this endpoint in our interactive playground with real data.