Cursor-paginated queries + SSE real-time subscriptions. Auth via X-API-Key header or ?api_key= query param.
Queries with cursor pagination
SSE real-time new-lead stream
Get your key from the dashboard → Settings → Private API Key.
type Query {
# Simple flat list (max 1000 results, no cursor)
leads(district: String): [Lead!]!
# Cursor-paginated — RECOMMENDED for production CRM integration
leadsConnection(
district: String # filter by Warsaw district name
first: Int # page size default: 20 max: 100
after: String # cursor from pageInfo.endCursor of previous page
): LeadsConnection!
totalCount: Int!
me: UserPlan!
}
type LeadsConnection {
edges: [LeadEdge!]!
pageInfo: PageInfo!
totalCount: Int! # total matching leads across ALL pages
}
type LeadEdge {
cursor: String! # opaque — pass as "after" to fetch next page
node: Lead!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String # pass this as "after" on next request
}
type Lead {
id String
source String # "olx" | "gratka"
title String
url String
price String
priceValue Float
district String
location String
image String
areaM2 Float
rooms String
floor String
createdAt String # RFC3339
isNew Boolean
}
type UserPlan {
uid String
email String
planId String
subscriptionStatus String
selectedDistricts [String]
}
First page — no after needed:
query Page1 {
leadsConnection(district: "Mokotow", first: 20) {
totalCount
pageInfo { hasNextPage endCursor }
edges {
cursor
node {
id title price priceValue
district location url source
areaM2 rooms floor createdAt
}
}
}
}
Next page — use endCursor as after:
query Page2 {
leadsConnection(district: "Mokotow", first: 20, after: "MTc5MDg1NzUwMzQ0ODow") {
pageInfo { hasNextPage endCursor }
edges { cursor node { id title price url createdAt } }
}
}
# First page
curl -X POST https://yourdomain.pl/api/graphql \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_KEY" \
-d '{"query":"{ leadsConnection(district:\"Mokotow\",first:20){ totalCount pageInfo{hasNextPage endCursor} edges{cursor node{id title price url createdAt}} } }"}'
# Next page (replace CURSOR with endCursor from above)
curl -X POST https://yourdomain.pl/api/graphql \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_KEY" \
-d '{"query":"{ leadsConnection(first:20,after:\"CURSOR\"){ pageInfo{hasNextPage endCursor} edges{cursor node{id title price url}} } }"}'
async function fetchAllLeads(district = 'Mokotow', pageSize = 50) {
const API = 'https://yourdomain.pl/api/graphql';
const KEY = 'YOUR_API_KEY';
let after = null;
const all = [];
do {
const afterPart = after ? ', after: "' + after + '"' : '';
const query = '{ leadsConnection(district: "' + district + '", first: ' + pageSize + afterPart + ') { pageInfo { hasNextPage endCursor } edges { node { id title price url district createdAt } } } }';
const res = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': KEY },
body: JSON.stringify({ query }),
});
const { data } = await res.json();
const conn = data.leadsConnection;
all.push(...conn.edges.map(e => e.node));
after = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;
} while (after);
return all; // all leads across all pages
}
curl -N -H "X-API-Key: YOUR_KEY" \ "https://yourdomain.pl/api/graphql/subscription?district=Mokotow"
const sse = new EventSource(
'/api/graphql/subscription?api_key=YOUR_KEY&district=Mokotow'
);
sse.addEventListener('next', (e) => {
const { data } = JSON.parse(e.data);
console.log('New lead:', data.leadAdded);
});
sse.addEventListener('connected', (e) => console.log('Connected:', JSON.parse(e.data)));
first per page: 100429 with Retry-After: 10