Search public tutti.ch classified ads. Filter by keyword, category id, canton code or locality id and a price range in CHF, and page with the opaque cursor. Each ad carries the id, title, integer CHF price (when priced) and price label, publish time (ISO 8601, UTC), category, location with canton, thumbnail, images and the seller alias. Pages hold 30 ads. A valid empty result is a successful response; an upstream failure is not billed.
Run this endpoint
Endpoint
Parameters
Start with the required fields, then add optional filters only when your use case needs them.
Runnable path
This endpoint has no required query parameters.
9 optional filters available.
string
Optional
Search keywords, up to 200 characters. At least one of query, category, canton or locality is required. Example: velo.
velo
string
Optional
Category id from /api/tutti/categories, root or sub category. Example: bicycles.
bicycles
string
Optional
Two-letter canton code: ZH, BE, LU, UR, SZ, OW, NW, GL, ZG, FR, SO, BS, BL, SH, AR, AI, SG, GR, AG, TG, TI, VD, VS, NE, GE or JU. Example: BE.
BE
string
Optional
Up to 5 comma-separated locality ids from /api/tutti/locations. Not combinable with canton. Example: geo-city-zurich.
example
integer
Optional
Minimum price in CHF, 0 to 10000000.
100
integer
Optional
Maximum price in CHF, 0 to 10000000, not below price_min.
500
string
Optional
desc for newest first (default) or asc for oldest first.
example
string
Optional
The next_cursor value of the previous response to load the next 30 ads.
0
string
Optional
Label language: de (default), fr or it.
en
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500",
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500');
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500', 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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500',
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500")
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500", 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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500', 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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500")
.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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500", 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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500"
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500"));
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500',
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/tutti/search?query=velo&category=bicycles&canton=BE&price_min=100&price_max=500")
.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
data
meta
{
"success": true,
"data": {
"total": 827,
"listings": [
{
"id": "83552223",
"title": "Woom 3 16 Zoll",
"price": 325,
"price_label": "325.-",
"published_at": "2026-10-15T18:52:49Z",
"category": {
"id": "bicycles",
"label": "Velos"
},
"location": {
"postcode": "3250",
"name": "Lyss",
"canton": {
"code": "BE",
"name": "Bern"
}
},
"thumbnail": "https://c.tutti.ch/big/2279645995.jpg",
"images": [
"https://c.tutti.ch/big/2279645995.jpg"
]
}
],
"has_more": true,
"next_cursor": "eyJ0IjoxNzkyMDkyOTA2LCJjdiI6ODYyODY4MTYzLCJvIjozMCwic20iOjAsInNhIjpmYWxzZX0="
},
"meta": {
"endpoint_family": "search",
"billable": true
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
Validation Error
No filter was given, or a canton, locality, price, direction or cursor value is invalid. This response is not billable.
{
"errors": {
"query": [
"Provide at least one of query, category, canton or locality."
]
},
"message": "The request validation failed"
}Invalid Category or Cursor
The category id is not in the category tree or the cursor is not a next_cursor of tutti.ch. This response is not billable.
{
"meta": {
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "INVALID_PARAMETER",
"message": "The category or cursor is not valid. Use /api/tutti/categories for category ids and the next_cursor of a previous response."
},
"success": false
}Unexpected Upstream Response
tutti.ch answered with an error or an unexpected shape. This response is not billable; retry later.
{
"meta": {
"billable": false,
"retryable": true,
"endpoint_family": "search"
},
"error": {
"code": "PARSER_DRIFT",
"message": "The tutti.ch API is temporarily unavailable. Please retry."
},
"success": false
}Upstream Unavailable
tutti.ch is temporarily unreachable or blocked the request. This response is not billable; retry later.
{
"meta": {
"billable": false,
"retryable": true,
"endpoint_family": "search"
},
"error": {
"code": "UPSTREAM_UNAVAILABLE",
"message": "The tutti.ch API 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.