Scrappa's GET /api/fincaraiz/search returns Colombian property listings for sale (operacion=venta) or for rent (operacion=arriendo) as structured JSON, instead of the HTML you would otherwise have to scrape.
What you get
One request returns a page of listings plus the platform's own match count in total. That count comes from the platform's search index and is the only number on this API that reflects the full result set — the totals on the enrichment endpoints do not, and the pagination fields are derived from this one.
Each listing is returned essentially as the platform publishes it: id, title, description, price (amount and currency), bedrooms, bathrooms, area, address, typeID, facilities, latitude/longitude, created_at, sold, soldDate, and the full nested locations tree. On top of that, images is a flat list of image URLs, image_count and has_images describe them, and active tells you whether the listing is currently live.
Titles are published with double spaces and are returned exactly as written.
Geography
Use locations, not a city name. Colombia uses two unrelated id vocabularies on this platform: search geography is a UUID, while the numbered estate ids used by the location vocabulary do not work here at all. Get the right value from /api/fincaraiz/location-autocomplete?term=medellin, which returns each match together with the exact id and type pair to pass back.
Each location needs both an id and a type, for example locations[0][id]=...&locations[0][type]=CITY. Omitting the type is rejected.
Filters that silently do nothing
This platform accepts a set of parameter names and ignores them completely: you get a normal 200 with a correct-looking result set and an unchanged total. Rather than pass that on, this API rejects them with a 422, along with any name it does not support. So a request that returns 200 has had every filter you asked for applied.
The fair_active flag is separately rejected under any spelling: it is an internal allowlist switch that reduces a corpus of more than 180,000 listings to roughly 130.
Ranges and units
Prices are whole Colombian pesos and are large — a typical apartment is in the hundreds of millions. Supply them as plain integers. Area is in square metres. Filters intersect: a minimum and maximum price narrow the result together rather than one replacing the other.
What this API does not cover
This is read-only listing data. There is no account access, no messaging or contact-form submission, and no listing creation. The platform publishes no reviews or ratings and no price history, so neither appears here. Sold and expired listings are not reachable through a filter: the platform has no lifecycle filter, so active, sold, and soldDate are per-listing flags you read rather than filter on.
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.
16 optional filters available.
integer
Optional
1 for sale (venta, the default) or 2 for rent (arriendo).
1
integer
Optional
One or more of 1 house, 2 apartment, 3 lot, 4 commercial premises. Repeatable, or comma-separated.
2
integer
Optional
Minimum price in Colombian pesos, as a whole number (e.g. 200000000).
10
integer
Optional
Maximum price in Colombian pesos.
400000000
integer
Optional
Minimum area in square metres.
10
integer
Optional
Maximum area in square metres.
10
integer
Optional
Minimum number of bedrooms (a count, not an id). Repeatable.
3
integer
Optional
Minimum number of bathrooms. Repeatable.
10
integer
Optional
Minimum number of rooms. Repeatable.
10
integer
Optional
Neighbourhood wealth bracket, 1-6. Repeatable.
10
string
Optional
One or more location objects, each with an id and a type, both required. Ids come from location-autocomplete. Format: locations[0][id]=UUID&locations[0][type]=CITY.
example
integer
Optional
Result ordering code. This sorts results and never changes the total.
10
integer
Optional
Restrict results to one agency's inventory.
1234567890
boolean
Optional
Restrict results to private (non-agency) sellers.
true
integer
Optional
Listings per page, 1-200 (default 20).
10
integer
Optional
Result page, 1-based. A page past the end returns a valid but empty page, not an error.
1
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10",
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10');
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10', 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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10',
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10")
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10", 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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10', 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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10")
.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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10", 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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10"
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10"));
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10',
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/fincaraiz/search?operation_type_id=1&property_type_id=2&maxPrice=400000000&bedrooms=3&rows=10")
.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
total
page
rows
returned
total_pages
applied_filters
property_types
+2 more
Common listings fields
id
title
price
bedrooms
{
"success": true,
"total": 83191,
"page": 1,
"rows": 10,
"returned": 10,
"total_pages": 8320,
"applied_filters": {
"operation_type_id": 1,
"property_type_id": [
2
],
"maxPrice": 400000000,
"bedrooms": [
3
]
},
"property_types": {
"1": "casa",
"2": "apartamento",
"3": "lote",
"4": "local"
},
"listings": [
{
"id": 191347339,
"title": "Apartamento en Venta en Manga, Cartagena",
"price": {
"amount": 880000000,
"currency": "COP"
},
"bedrooms": 3,
"bathrooms": 2,
"area": 120,
"typeID": 2,
"active": true,
"sold": false,
"soldDate": null,
"image_count": 12,
"has_images": true,
"images": [
"https://cdn4.fincaraiz.com.co/repo/img/example.jpg"
],
"latitude": 10.4236,
"longitude": -75.5478
}
],
"meta": {
"billable": true,
"endpoint_family": "search",
"attempts": 1
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
Unsupported Filter
A filter name this API does not support, or one the platform accepts and silently ignores, was supplied (`unknown_filter`, `blocked_filter`). Non-billable.
{
"meta": {
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "unknown_filter",
"message": "price_min is not a supported filter. Unknown filters are ignored by the platform, so they are rejected instead of silently dropping the filter."
},
"success": false
}Validation Error
A query parameter failed validation. Non-billable.
{
"errors": {
"operation_type_id": [
"The operation type field is invalid."
]
},
"message": "The operation type field is invalid."
}Upstream Unavailable
The upstream request failed or could not be parsed after retries. Retryable and never billed. Clients are never rate limited.
{
"meta": {
"attempts": 4,
"billable": false,
"retryable": true,
"last_status": 503,
"endpoint_family": "search"
},
"error": {
"code": "upstream_unavailable",
"message": "Fincaraiz is temporarily unavailable. Please retry."
},
"success": false
}Unreadable Response
Fincaraiz answered 200 with a body this endpoint cannot read (`parser_drift`). Nothing is charged for it; an empty or reshaped upstream payload is reported rather than published as an empty result. Retryable.
{
"meta": {
"billable": false,
"retryable": true,
"endpoint_family": "search"
},
"error": {
"code": "parser_drift",
"message": "Fincaraiz returned an unreadable response. 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.
Related Endpoints
Fincaraiz API FAQ
Does Fincaraiz offer an official API?
No. Fincaraiz publishes no developer API. Scrappa provides a documented read-only API over its public listing surfaces and returns normalized JSON with API key authentication.
Why does my filter get rejected when the website accepts it?
Several parameter names the platform accepts have no effect: the response is a normal 200 with an unchanged total, so the filter would silently do nothing. Those names, plus fair_active, are rejected with a 422 instead. Any request that returns 200 has had all of its filters applied.
Are empty results charged?
A 200 response with zero listings is a valid answer and is billed. Failures — rejected filters, validation errors, and upstream outages — never consume credits.
Can I search by city name?
Not directly. Pass ids and types from the location-autocomplete endpoint. The numbered estate ids from the location vocabulary are a different set and return no results in search.
Try It Live
Test this endpoint in our interactive playground with real data.