Sync strategy
The recommended method to sync Pylote freelancers into your database:
Initial sync
Use modifiedTime=0 to fetch all freelancers, then paginate:
const axios = require('axios');
async function syncAllFreelances() {
const API_URL = 'https://client-p.pylote.io/freelances';
const API_KEY = process.env.PYLOTE_API_KEY;
const limit = 1000;
let page = 1;
let totalPages = 1;
let allFreelances = [];
while (page <= totalPages) {
const response = await axios.get(API_URL, {
headers: { 'x-api-key': API_KEY },
params: { modifiedTime: 0, page, limit, showDeleted: false }
});
allFreelances.push(...response.data.freelances);
totalPages = response.data.infos.totalPages;
page++;
}
console.log(`${allFreelances.length} freelancers synced`);
return allFreelances;
}
Incremental sync
Store the timestamp of your last sync, then fetch only the profiles
modified since then:
async function syncUpdatedFreelances(lastSyncTimestamp) {
const response = await axios.get(API_URL, {
headers: { 'x-api-key': API_KEY },
params: {
modifiedTime: lastSyncTimestamp, // Unix timestamp in seconds
page: 1,
limit: 1000,
showDeleted: true // include deleted profiles
}
});
for (const freelance of response.data.freelances) {
if (freelance.meta.status === 'deleted') {
// REQUIRED: delete from your database
await deleteFromYourDB(freelance.meta.id);
} else {
await upsertInYourDB(freelance);
}
}
// Store the new timestamp for the next sync
return Math.floor(Date.now() / 1000);
}
Freelancers with status: deleted must be deleted from your database
within 30 days. This is a contractual commitment.
The possible deletion statuses are:
account_deleted, account_banned, account_excluded, account_invisible.
Responses are paginated with the page and limit parameters.
Each response includes an infos object:
{
"freelances": [...],
"infos": {
"total": 1234,
"page": 1,
"limit": 50,
"totalPages": 25
}
}
| Parameter | Type | Required | Description |
|---|
page | integer | Yes | Page number (starts at 1) |
limit | integer | Yes | Freelancers per page (max 1000) |
modifiedTime | integer | Yes | Unix timestamp (seconds). 0 = all |
showDeleted | boolean | No | Include deleted profiles (default: true) |
Use limit=1000 to minimize the number of requests.
An initial sync of ~22,000 freelancers takes about 22 pages.
Deduplication
The same freelancer may be present on several platforms. The meta.personalEmail
field (SHA-256 hash of the personal email) lets you deduplicate:
// If two profiles share the same personalEmail, it's the same freelancer.
// Keep the one with the most recent updatedAt.
const seen = new Map();
for (const f of freelances) {
const hash = f.meta.personalEmail;
if (!seen.has(hash) || f.meta.updatedAt > seen.get(hash).meta.updatedAt) {
seen.set(hash, f);
}
}
Tracked URLs (hive.pylote.io)
The basics.url field (LinkedIn) and the CV link in basics.profiles go through
the hive.pylote.io proxy. These URLs:
- Track profile views for the freelancer’s stats
- Contain a watermark (the caller’s IP encoded in base64)
- Must not be shared outside your platform
Format:
https://hive.pylote.io/linkedin/{encrypted_id}/{clientPk}?t={watermark}
https://hive.pylote.io/cv/{encrypted_id}/{clientPk}?t={watermark}
Fetching specific freelancers
If you need to re-sync a subset of profiles
(for example after an incident), use POST /freelances with a list of IDs:
curl -X POST "https://client-p.pylote.io/freelances?modifiedTime=0&page=1&limit=100" \
-H "x-api-key: your-key" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"bc1d49cf-1cc7-4afe-92aa-0ebd545fcd12",
"960a1c67-645d-4ac5-b8b2-875fb2f740d3"
]
}'
Recommended sync frequency
| Strategy | Frequency | Use case |
|---|
| Classic cron | Every hour | Enough for most clients |
| Near real-time | Every 15 min | If availability freshness is critical |
| Initial sync | Once | First population of your database |
The API cache is refreshed every hour. Syncing more often than
hourly brings no additional data.