Skip to content
Scrappa Get API key
Metrocuadrado API 1 credit/request

Metrocuadrado Locations API Documentation

GET https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout

Scrappa's GET /api/metrocuadrado/locations endpoint resolves Colombian place names, in two modes. Each request performs one lookup; the modes are alternatives, not a chain.

mode=autocomplete (default) takes free text such as bogota and returns the source's six suggestion levels — cities, neighbourhoods, points, regions, sectors and zones — each with a display label and the token to use in a search. This is what a type-ahead field should call.

mode=fanout takes a place token and returns the source's own vocabulary for that place — useful for resolving city and zone tokens, and for autocomplete. Pass level=zones (default), level=cities or level=neighborhoods. The resolved vocabulary is cached for 30 minutes, so repeated lookups of the same city cost one lookup, not one per request.

It does not return a complete neighbourhood partition and must not be used as one. The source caps the neighbourhood list at 100 rows and does not scope it to the queried city: for q=bogota it returns an alphabetical slice of a global vocabulary and omits most barrios, including Chapinero. level=zones is city-scoped, but its union covers only 56.1 % of the Bogotá venta/apartment cell (16,218 of 28,888, with Norte alone at 9,598 — 93 % of the 10,000 page ceiling). A sweep built on either list silently misses rows, so the response sets is_complete_enumeration: false on every call.

neighborhood remains a valid filter value — a bare slug; chapinero matches 769 listings. To subdivide a large cell without silent loss, use the radius grid (latitude, longitude, distance) on the search endpoint: inside a radius every returned listing is geo-referenced. The radius grid is best effort over the geo-referenced subset — 98.8 % for that cell and 89.9 % nationally — so it is not lossless, and no partition available here is provably exhaustive. Start the grid at 3 km or tighter; at 5 km the cell is past the row ceiling again.

A token is not a URL fragment. Passing bogota/chapinero where chapinero is expected is rejected with a 422 rather than answered with an empty page.

A term the source does not know returns a non-billable error rather than an empty success.

Metrocuadrado Locations API Documentation 1 credit/request

Endpoint

Request preview
GET
https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout
Auth header
x-api-key
Cost
1 credit/request
q = bogota
Response preview
200 OK
{
    "success": true,
    "mode": "fanout",
    "query": "bogota",
    "level": "zones",
    "count": 1,
    "values": [
        {
            "token": "norte",
            "label": "Norte",
            "city": "bogota",
            "zone": "norte",
            "neighborhood": null,
            "latitude": null,
...

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.

2 optional filters available.

q string Required

Free text in autocomplete mode, or a place token in fanout mode.

Example value bogota
mode string Optional

autocomplete (default) or fanout.

Example value fanout
level string Optional

fanout mode only: zones (default, city-scoped), cities, or neighborhoods. The neighborhood list cannot enumerate a city.

Example value example

Request Examples

PHP
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => "https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout",
    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
<?php

use Illuminate\Support\Facades\Http;

$response = Http::timeout(30)
    ->withHeaders(['x-api-key' => 'YOUR_API_KEY_HERE'])
    ->get('https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout');

if ($response->successful()) {
    echo $response->body();
} else {
    echo "Error: " . $response->status();
}
JavaScript
const options = {
    method: 'GET',
    headers: {
        'x-api-key': 'YOUR_API_KEY_HERE'
    }
};

fetch('https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout', 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));
JavaScript
const axios = require('axios');

const options = {
    method: 'GET',
    url: 'https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout',
    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);
}
Ruby
require 'net/http'
require 'uri'

uri = URI.parse("https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout")
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
Python
import http.client
import json

conn = http.client.HTTPSConnection("scrappa.co")

headers = {
    'x-api-key': 'YOUR_API_KEY_HERE',
}

try:
    conn.request("GET", "/api/metrocuadrado/locations?q=bogota&mode=fanout", headers=headers)
    res = conn.getresponse()
    data = res.read()
    print(data.decode("utf-8"))
except Exception as e:
    print(f"Error: {e}")
finally:
    conn.close()
Python
import requests

headers = {
    'x-api-key': 'YOUR_API_KEY_HERE',
}

try:
    response = requests.get('https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout', headers=headers)
    response.raise_for_status()
    print(response.text)
except requests.exceptions.RequestException as e:
    print(f"Error: {e}")
Java
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/metrocuadrado/locations?q=bogota&mode=fanout")
        .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());
        }
    }
}
Go
package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {
    client := &http.Client{}
    req, err := http.NewRequest("GET", "https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout", 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))
}
Terminal
#!/bin/bash

curl -X GET \
    -H "x-api-key: YOUR_API_KEY_HERE" \
    "https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout"
C#
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/metrocuadrado/locations?q=bogota&mode=fanout"));
            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}
TypeScript
import axios from 'axios';

async function run(): Promise<void> {
    try {
        const response = await axios({
            method: 'GET',
            url: 'https://scrappa.co/api/metrocuadrado/locations?q=bogota&mode=fanout',
            headers: {
        'x-api-key': 'YOUR_API_KEY_HERE',
            },
        });

        console.log(response.data);
    } catch (error) {
        console.error('Error:', error);
    }
}

void run();
RUST
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/metrocuadrado/locations?q=bogota&mode=fanout")
        .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 mode query level count values is_complete_enumeration available_levels +2 more

Common values fields

token label city zone
JSON Response
200 OK
{
    "success": true,
    "mode": "fanout",
    "query": "bogota",
    "level": "zones",
    "count": 1,
    "values": [
        {
            "token": "norte",
            "label": "Norte",
            "city": "bogota",
            "zone": "norte",
            "neighborhood": null,
            "latitude": null,
            "longitude": null
        }
    ],
    "is_complete_enumeration": false,
    "available_levels": [
        "cities",
        "zones",
        "neighborhoods"
    ],
    "did_you_mean": null,
    "meta": {
        "endpoint_family": "locations",
        "fanout_axes": [
            "business_type",
            "property_type",
            "city",
            "neighborhood"
        ],
        "fanout_axis_sources": {
            "business_type": "client-side value; list them at /api/metrocuadrado/types?type=deal",
            "property_type": "client-side value; list them at /api/metrocuadrado/types?type=property",
            "city": "token from this endpoint (level=cities)",
            "neighborhood": "a working filter on /search by bare slug, but its partition is NOT available from this endpoint - enumerate with the radius grid instead"
        },
        "note": "Use the token in `location` for the city and neighborhood parameters of /search. URL fragments are a different vocabulary and are not accepted there.",
        "partition_method": "To split a large city, subdivide by radius instead: pass latitude, longitude and distance to /search. Inside a radius every returned listing is geo-referenced, so the grid cannot silently drop rows it can see. Start the grid at 3 km or tighter: at 5 km a large cell already exceeds the row ceiling again. It is still not an exhaustive sweep: listings carrying no coordinates are unreachable by radius (roughly 2 percent of sale and 6 percent of rent inventory carries none), so no partition here is provably complete. Do not build one from this vocabulary."
    }
}

Errors

Handle these documented responses before retrying or showing customer-facing failures.

422

HTTP 422

The term is missing, is a URL fragment in fanout mode, or an unknown parameter was sent. Nothing is charged.

404

HTTP 404

The source knows no location for this term. Not charged.

503

HTTP 503

The source was unreachable. Not charged, safe to retry.

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.