REST API · v2

Vocations
API Docs

Integra il monitoraggio delle offerte di lavoro e l'intelligence sulle aziende direttamente nel tuo workflow di recruiting.

Introduzione

Autenticazione

Tutti gli endpoint sotto /v2 richiedono API key valida, anche quelli che non consumano utilizzo.

Gli endpoint sotto /playground non richiedono API key.

Per gli endpoint /v2, la chiave deve essere passata nell'header HTTP Authorization con il valore Bearer <chiave>.

http header
Authorization: Bearer 5tg••••••••••••••••8ux
Introduzione

Tipi condivisi

ℹ️

? dopo qualsiasi chiave di un tipo implica che il valore è opzionale.
[] dopo qualsiasi tipo implica che il valore, se passato, deve essere un array.
Tutti gli id hanno lunghezza di 16 caratteri.
Gli endpoint di ricerca accettano page (in base zero, default 0) e pageSize (minimo 1, massimo 128, default 10).
Gli array dei filtri accettano fino a 64 elementi, tranne names, cities, provinces, regions, nations, postalCodes, italy.ivas, italy.ateco, spain.cifNifs e spain.cnae, che ne accettano fino a 256. Gli endpoint batch accettano da 1 a 128 ID. Superare un limite restituisce 400.
Ogni risposta autenticata restituisce X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset per la finestra del minuto corrente. Un 429 restituisce anche Retry-After.

shared types
type ErrorResponse = { message: string };

type CountryKey = "italy" | "spain";

type LocationLevel = {
  level: 4 | 6 | 8;
  value: string;
};

type LocationFilter = {
  country: CountryKey;
  levels: LocationLevel[];
};

type Location = {
  city?: string;
  province?: string;
  region?: string;
  nation?: string;
  latitude?: number;
  longitude?: number;
};

type PendingStatus =
  | "idle"
  | "pending"
  | "completed"
  | "notAvailable"
  | "failed";

type NumericRange =
  | { min: number; max: number }
  | { min: number; max?: undefined }
  | { min?: undefined; max: number };

type YearlySalaryRange = {
  min?: number;
  max?: number;
  currency: string;
};

type YearlyValue = {
  year: number;
  value: number;
};

type SortType =
  | "default"
  | "name"
  | "city"
  | "province"
  | "region"
  | "sectors"
  | "currentTotalRevenue"
  | "currentEmployees"
  | "currentOpenPositions";

type SortOrder = "asc" | "desc";

type PositionSource = "vocations" | "linkedIn" | "indeed" | "infoJobs";

type Hours = "full" | "part" | "shift";

type CompanyStatus = "open" | "bankruptcy" | "liquidation";

type ValueTrend = "increasing" | "decreasing" | "stable";

type ExperienceLevel = "0" | "0-1" | "1-3" | "3-5" | "5-10" | "10+";

type Contract =
  | "temporary"
  | "internship"
  | "permanent"
  | "contract"
  | "apprenticeship"
  | "freelance";

type Collar = "blue" | "white";

type SmartWorking = "yes" | "no" | "hybrid";

type BusinessUnit =
  | "finance"
  | "hr"
  | "purchasing"
  | "marketing"
  | "sales"
  | "customerService"
  | "research"
  | "operations"
  | "it"
  | "legal"
  | "strategy"
  | "sustainability"
  | "administration";

type SectorKey =
  | "administrationAccounting"
  | "textileClothing"
  | "mediaCommunicationMarketing"
  | "aeronauticsNavalDefense"
  | "realEstate"
  | "agriculture"
  | "horeca"
  | "food"
  | "artHistoryArchitecture"
  | "furniture"
  | "craftsmanship"
  | "insurance"
  | "socialCooperativesAssociations"
  | "automotive"
  | "banking"
  | "biomedical"
  | "broadcastingMusicFilm"
  | "outsourcingBusinessServices"
  | "retail"
  | "manufacturingIndustrial"
  | "chemical"
  | "energyOilGas"
  | "trade"
  | "consultingITConsulting"
  | "consumerGoods"
  | "pharmaceutical"
  | "environmentSustainabilityEcology"
  | "publishing"
  | "educationTraining"
  | "electronicsElectrotechnics"
  | "industrialEngineeringServices"
  | "sportsCultureEntertainment"
  | "publicEntitiesNonProfitPublicAdmin"
  | "financeFinancialServices"
  | "gamingBetting"
  | "rubberPlastic"
  | "largeScaleRetail"
  | "healthHygiene"
  | "humanResources"
  | "warehousePackaging"
  | "cybersecurityIT"
  | "legalCompliance"
  | "logistics"
  | "transportShipping"
  | "medical"
  | "metallurgyMechanics"
  | "exhibitionsFairsEvents"
  | "motors"
  | "cleaning"
  | "securitySurveillance"
  | "personalServices"
  | "professionalFirm"
  | "telecommunications"
  | "tourismTravel"
  | "designFashion"
  | "multiservices"
  | "technologyScienceData"
  | "automation"
  | "other";
  
  type ItalyLegalForm =
  | "AA"
  | "AC"
  | "AE"
  | "AF"
  | "AI"
  | "AL"
  | "AM"
  | "AN"
  | "AP"
  | "AR"
  | "AS"
  | "AT"
  | "AU"
  | "AZ"
  | "CC"
  | "CE"
  | "CF"
  | "CI"
  | "CL"
  | "CM"
  | "CN"
  | "CO"
  | "CR"
  | "CS"
  | "CZ"
  | "DI"
  | "EC"
  | "ED"
  | "EE"
  | "EI"
  | "EL"
  | "EM"
  | "EN"
  | "EP"
  | "ER"
  | "ES"
  | "FI"
  | "FO"
  | "GE"
  | "IC"
  | "ID"
  | "IF"
  | "IR"
  | "LL"
  | "MA"
  | "OC"
  | "OO"
  | "OS"
  | "PA"
  | "PC"
  | "PF"
  | "PS"
  | "RC"
  | "RR"
  | "RS"
  | "SA"
  | "SC"
  | "SD"
  | "SE"
  | "SF"
  | "SG"
  | "SI"
  | "SL"
  | "SM"
  | "SN"
  | "SO"
  | "SP"
  | "SR"
  | "SS"
  | "ST"
  | "SU"
  | "SV"
  | "SZ";

type SpainLegalForm =
  | "GE"
  | "IE"
  | "IU"
  | "PO"
  | "AC"
  | "CJ"
  | "CB"
  | "CD"
  | "BI"
  | "PH"
  | "CG"
  | "CL"
  | "EX"
  | "PV"
  | "NL"
  | "NR"
  | "PJ"
  | "FN"
  | "FJ"
  | "FD"
  | "MT"
  | "MU"
  | "OA"
  | "PP"
  | "AG"
  | "SM"
  | "SA"
  | "ES"
  | "SD"
  | "SE"
  | "SB"
  | "SF"
  | "SU"
  | "CV"
  | "CP"
  | "CO"
  | "CA"
  | "CS"
  | "SC"
  | "SG"
  | "GR"
  | "LD"
  | "SL"
  | "LL"
  | "LP"
  | "NE"
  | "LF"
  | "LU"
  | "UC"
  | "UT"
  | "OR";
Introduzione

Playground

Usa il playground per esplorare gli endpoint senza API key. Il base path del playground è https://vocations.io/api/playground, rispecchia esattamente gli endpoint v2 e usa dati sintetici italiani e spagnoli.

API Playground — vocations.io
Nessuna API key richiesta
Aziende
Posizioni
Contatti
Utilizzo
POSTvocations.io/api/playground/companies
curl -X POST https://vocations.io/api/playground/companies \
  -H "Content-Type: application/json" \
  -d '{
    "page": 0,
    "company": {
      "locations": [
        {
          "country": "spain",
          "levels": [{ "level": 4, "value": "Cataluña" }]
        }
      ],
      "sectors": ["technologyScienceData"],
      "employees": [{ "min": 11, "max": 200 }]
    },
    "sortType": "name",
    "sortOrder": "asc",
    "withPositions": true
  }'
🔒 Nessuna chiave necessaria nel playground
Risposta200 OK
{
  "companyIDs": [
    "pc0000000000000f",
    "pc0000000000001j",
    "pc0000000000002b"
  ],
  "totalPages": 22,
  "totalResults": 217
}
Questo è un'anteprima visiva. Per interagire con il vero playground vai su vocations.io/api/playground → vocations.io/api/playground →
1.2 Arricchimento Aziende

POST /companies/enrich

💳 1 credito/azienda · 2 crediti con dati finanziari

Esegue l'enrich delle aziende tramite gli ID passati.

Richiesta

request type
type V2CompanyEnrichRequest = {
  companyIDs: string[];
};

Risposta 200

response
type V2EnrichedCompany = {
  companyID: string;
  parentHolding?: string;
  name: string;

  workAgency: boolean;
  openPositionCount: number;

  url?: string;
  imageFile?: string;
  linkedInURL?: string;
  indeedURL?: string;
  infoJobsURL?: string;

  address?: string;
  location?: LocationLevel[];
  nation?: string;

  sectors: SectorKey[];

  employees: YearlyValue[];

  totalRevenue: YearlyValue[];
  employeesCost: YearlyValue[];
  netProfit: YearlyValue[];

  italy?: {
    ragioneSociale: string;
    iva?: string;
    ateco?: string;
    atecoCode?: string;
    rea?: string;
    shareCapital?: number;
    legalForm?: ItalyLegalForm;
    chamberOfCommerce?: string;
  };

  spain?: {
    razonSocial: string;
    cifNif?: string;
    cnae?: string;
    cnaeCode?: string;
    legalForm?: SpainLegalForm;
    shareCapital?: number;
  };

  createdAt?: number;
  validationKey: string;
};

type V2CompanyEnrichResult = {
  companies: V2EnrichedCompany[];
  notFoundCompanyIDs: string[];
  insufficientCreditsCompanyIDs: string[];
};
ℹ️

imageFile contiene solo il nome del file. Per accedere al logo usa la CDN: https://cdn.vocations.io/logos/<imageFile>

ℹ️

notFoundCompanyIDs elenca gli ID inesistenti o non visibili alla tua chiave. insufficientCreditsCompanyIDs elenca le aziende esistenti escluse perché i crediti sono terminati durante il batch: richiedile di nuovo quando hai crediti. Un 402 viene restituito solo quando nessuna azienda richiesta era coperta dai crediti.

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme.
401
Unauthorized
API key mancante o non valida.
429
Rate limit exceeded
500
Internal server error
1.3 Stato Aziende

POST /companies/status

✓ Non consuma crediti
Rate limit: max 60 req/min · 3600 req/ora

Verifica se i dati che hai già sono ancora aggiornati. Invia ogni companyID insieme alla validationKey restituita da /companies/enrich.

Richiesta

request type
type V2CompanyStatusRequest = {
  companies: {
    companyID: string;
    validationKey: string;
  }[];
};

Esempio body (JSON)

json
{
  "companies": [
    {
      "companyID": "cmp1a2b3c4d5e6f7g",
      "validationKey": "eyJhbGciOiJIUzI1NiJ9..."
    },
    {
      "companyID": "cmp8h9i0j1k2l3m4n",
      "validationKey": "eyJhbGciOiJIUzI1NiJ9..."
    }
  ]
}

Risposta 200

response
type V2CompanyStatusResult = {
  upToDateIDs: string[];
  outdatedIDs: string[];
  invalidIDs: string[];
};
💡

outdatedIDs contiene le aziende i cui dati sono cambiati dopo l'emissione della validationKey: vanno rifatte con enrich. invalidIDs contiene aziende sconosciute, o chiavi che non appartengono al companyID con cui sono state inviate.

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme
401
Unauthorized
API key mancante o non valida
429
Rate limit exceeded
500
Internal server error
2.2 Arricchimento Posizioni

POST /positions/enrich

💳 1 credito ogni 5 posizioni

Richiesta

request type
type V2PositionEnrichRequest = {
  positionIDs: string[];
};

Risposta 200

response
type V2PositionCompanyContext = {
  companyID: string;
  workAgency: boolean;
  italy?: { ragioneSociale: string; iva?: string };
  spain?: { razonSocial: string; cifNif?: string };
};

type V2EnrichedPosition = {
  positionID: string;

  companyContext: V2PositionCompanyContext;

  name: string;
  description: string;

  urls: string[];
  sources: PositionSource[];

  targets: string[];

  cities: string[];
  provinces: string[];
  regions: string[];

  sites: Location[];

  skills: string[];
  benefits: string[];
  languages: string[];
  certifications: string[];
  courses: string[];

  experience?: ExperienceLevel;
  contract?: Contract;
  hours?: Hours;
  smartWorking: SmartWorking;
  collar?: Collar;
  protectedCategories?: boolean;

  yearlySalaryRange?: YearlySalaryRange;

  createdAt: number;
  republishedAt?: number;

  companyName?: string;

  validationKey: string;
};

type V2PositionEnrichResult = {
  positions: V2EnrichedPosition[];
  failedPositionIDs: string[];
};
ℹ️

Per ottenere i dati completi delle aziende usa l'endpoint /companies/enrich.

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme
401
Unauthorized
API key mancante o non valida
402
Not enough credits
429
Rate limit exceeded
500
Internal server error
2.3 Verifica Posizioni

POST /positions/check

✓ Non consuma crediti
Rate limit: max 60 req/min · 3600 req/ora

Per ottenere lo stato di una o più posizioni.

Richiesta

request type
type V2PositionsCheckRequest = {
  positionIDs: string[];
};

Risposta 200

response
type V2PositionCheckResult = {
  openIDs: string[];
  closedIDs: string[];
  invalidIDs: string[];
};

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme
401
Unauthorized
API key mancante o non valida
429
Rate limit exceeded
500
Internal server error
3.2 Arricchimento Contatti

POST /contacts/enrich

💳 3 crediti · +2 crediti con numero di telefono
Rate limit: max 20 req/min · 600 req/ora

Richiesta

request type
type V2ContactEnrichRequest = {
  contactID: string;
  enrichPhoneNumber?: boolean;
};

Risposta 200

response
type V2EnrichedContact = {
  contactID: string;

  firstName?: string;
  lastName?: string;

  jobTitle?: string;
  headline?: string;

  linkedInURL?: string;
  photoURL?: string;

  departments: string[];

  email?: string;
  emailStatus?: PendingStatus;

  mobilePhone?: string;
  phoneStatus?: PendingStatus;

  location: LocationLevel[];

  validationKey: string;
};

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme
401
Unauthorized
API key mancante o non valida
402
Not enough credits
404
Contact not found
Contatto non trovato.
429
Rate limit exceeded
500
Internal server error
3.3 Aggiornamento Contatti

POST /contacts/poll

💳 2 crediti quando arriva un numero
Rate limit: max 20 req/min · 600 req/ora

Restituisce lo stato attuale di un contatto già arricchito. Da usare quando enrichPhoneNumber ha restituito phoneStatus: "pending": la ricerca del numero è asincrona e arriva qui una volta risolta.

Richiesta

request type
type V2ContactPollRequest = {
  contactID: string;
};

Risposta 200

response
type V2ContactPollResult = {
  email?: string;
  emailStatus?: PendingStatus;

  mobilePhone?: string;
  phoneStatus: PendingStatus;
};
💡

I 2 crediti del telefono vengono addebitati solo quando un numero viene effettivamente restituito. Una ricerca che si chiude con notAvailable o failed non costa nulla.

Possibili errori

400
Bad request
Body JSON non valido o payload non conforme
401
Unauthorized
API key mancante o non valida
402
Not enough credits
404
Contact not found
Contatto non trovato, o mai arricchito.
429
Rate limit exceeded
500
Internal server error
4. Utilizzo

Utilizzo

GET /usage

✓ Non consuma crediti
Rate limit: max 10 req/min · 300 req/ora

Restituisce lo stato di utilizzo corrente della API key.

GEThttps://vocations.io/api/v2/usage

Risposta 200

response
type V2UsageSnapshot = {
  usedCredits: number;
  maxCredits: number;

  areCreditsShared: boolean;
  lastResetAt: number;
};

Possibili errori

401
Unauthorized
API key mancante o non valida.
429
Rate limit exceeded
500
Internal server error