---
title: "Match multiple candidates against a single offer (max 5000)"
method: POST
path: "/v1/match-candidates"
tags: ["matching"]
---

# Match multiple candidates against a single offer (max 5000)

`POST /v1/match-candidates`

## Request

- Base URL: `https://api.getyourjob.pro` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getyourjob.pro/v1/match-candidates`
- Auth: none declared

Match multiple candidates (max 5000) against a single job offer.
    
    **Input modes** (mutually exclusive):
    - **Structured**: Provide candidate/offer fields directly (name, skills, experience_years, etc.)
    - **Bulk**: Provide raw text/JSON in 'bulk' field - we extract structured data automatically
    
    **Pagination for large requests (> page_size candidates):**
    - The `page_size` option (default 100, range: 5-100) controls pagination threshold
    - Layer 1 quick sorting is applied to ALL candidates
    - Layer 2 deep analysis is run on the first page (page_size candidates)
    - A `matching_session_id` is returned for fetching additional pages
    - Use `POST /v1/matching-session` with session_id and page number for more results
    
    **Response type:**
    - <= page_size candidates: Returns `BatchMatchResultResponse` with direct deep analysis
    - > page_size candidates: Returns `PaginatedMatchResultResponse` with session ID

## Request body

- BatchMatchRequest: Request body for the /match-candidates endpoint. Contains multiple candidates (max 10000) and a single offer to match against. For requests with > page_size candidates (page_size default 100, range: 5-100): - Layer 1 quick sorting is applied to all candidates - Layer 2 deep analysis is run on the first page (page_size candidates) - A matching_session_id is returned for pagination - Use POST /v1/matching-session with session_id and page for additional pages Both candidates and offer support structured mode or bulk mode (mutually exclusive).
  - `candidates` CandidateRequest[], required: Candidates to match against the offer (max 20000, paginated)
    - `bulk` string, nullable: Raw text or JSON blob to extract candidate data from. Can be a CV, LinkedIn profile text, or any unstructured data. Mutually exclusive with other fields - use bulk OR structured fields, not both.
    - `external_id` string, nullable: Customer's external ID for data reconciliation (any format). Always allowed.
    - `name` string, nullable: Candidate name
    - `headline` string, nullable: Professional headline
    - `summary` string, nullable: Bio/summary
    - `experience_years` number, nullable: Total years of experience
    - `current_role` string, nullable: Current job title
    - `seniority` string, nullable: Seniority level: junior, mid, senior, lead, or principal
    - `location` string, nullable: Current location
    - `location_lat` number, nullable: Location latitude (decimal degrees)
    - `location_lng` number, nullable: Location longitude (decimal degrees)
    - `willing_to_relocate` boolean, nullable: Whether candidate is willing to relocate
    - `preferred_remote` string, nullable: Candidate's preferred remote work arrangement
    - `salary_expectation_min` integer, nullable: Minimum expected salary (annual, in local currency)
    - `salary_expectation_max` integer, nullable: Maximum expected salary (annual, in local currency)
    - `salary_currency` string, nullable: Currency code for salary (e.g., EUR, USD)
    - `daily_rate_expectation` integer, nullable: Expected daily rate (freelance), compared only with a daily-rate offer
    - `skills` string[], nullable: List of all skills (backward compat — use main_skills + secondary_skills when possible)
    - `main_skills` string[], nullable: Primary/core technical skills (weighted higher in matching)
    - `secondary_skills` string[], nullable: Secondary/tool skills (weighted lower in matching)
    - `main_skills_ranked` RankedSkillRequest[], nullable: Primary skills with their priority order (1 = strongest). When provided, supersedes positional ordering of main_skills.
      - `technology` string, required: Skill / technology name
      - `order` integer, required: Priority order; 1 is strongest
    - `secondary_skills_rated` RatedSkillRequest[], nullable: Secondary skills with self-reported proficiency 0-9. Layer 2 SkillsAgent scales each match by rating/9.
      - `technology` string, required: Skill / technology name
      - `rating` integer, required: Proficiency rating 0-9 (9 = expert)
    - `interested_roles` string[], nullable: Candidate's explicit role preferences as GetYourJob RoleType enum values (e.g. ['PRODUCT_OWNER', 'CONSULTANT', 'BACKEND']; also 'PRODUCT_DESIGNER' for product/UX/UI designers and 'BUSINESS_DEVELOPER' for IT sales). When set, matches are biased toward these roles: a bonus on on-role offers, a penalty on off-role offers. Free strings: a value this service does not know is accepted and ignored (no bonus, no penalty). Default null preserves prior scoring behavior.
    - `mobility` CandidateMobilityRequest: The candidate's declared reach (GetYourJob offerReach.ts): `radius_km` for on-site / hybrid offers (or `onsite_unlimited`), `remote_reach` NATIONAL | INTERNATIONAL for remote ones, `accepted_work_types` (remote / hybrid / onsite). Read by the analysis prose and the Layer 2 distance multiplier. Lenient: an unknown value is dropped, never a 422 that would fail the whole match request.
      - `radius_km` number, nullable: Travel radius (km) for on-site / hybrid offers; null = none stated.
      - `onsite_unlimited` boolean: On-site / hybrid offers anywhere (« sans limite »).
      - `remote_reach` 'NATIONAL' | 'INTERNATIONAL', nullable: Remote offers: NATIONAL (own country) or INTERNATIONAL (any country).
      - `accepted_work_types` string[], nullable: Work types accepted (remote / hybrid / onsite).
    - `experience` ExperienceRequest[], nullable: Detailed experience history
      - `title` string, nullable: Job title
      - `company` string, nullable: Company name
      - `duration_months` integer, nullable: Duration in months
      - `description` string, nullable: Role description
      - `skills_used` string[]: Skills used in this role
    - `education` EducationRequest[], nullable: Education history
      - `degree` string, nullable: Degree name
      - `school` string, nullable: School name
      - `year` integer, nullable: Graduation year
  - `offer` OfferRequest, required: Job offer data in a match request. Supports two mutually exclusive input modes: 1. **Structured mode**: Provide fields directly (title, required_skills, etc.) 2. **Bulk mode**: Provide raw text/JSON in 'bulk' field - we extract structured data Cannot mix bulk with other fields. Use one mode or the other.
    - `bulk` string, nullable: Raw text or JSON blob to extract offer data from. Can be a job posting, job description text, or any unstructured data. Mutually exclusive with other fields - use bulk OR structured fields, not both.
    - `external_id` string, nullable: Customer's external ID for data reconciliation (any format). Always allowed.
    - `title` string, nullable: Job title
    - `company` string, nullable: Company name
    - `description` string, nullable: Full job description
    - `required_skills` string[], nullable: Must-have skills
    - `nice_to_have_skills` string[], nullable: Bonus skills
    - `primary_skill` string, nullable: Explicit must-have skill that triggers the primary-skill-missing penalty. When None, the first non-sentinel required skill is used; the role-aware resolver (title/domain) applies only when `e1_primary_resolver_enabled` is on.
    - `seniority_required` string, nullable: Required seniority level: junior, mid, senior, lead, or principal
    - `experience_years_min` integer, nullable: Minimum years of experience
    - `experience_years_max` integer, nullable: Maximum years of experience
    - `location` string, nullable: Job location
    - `location_lat` number, nullable: Location latitude (decimal degrees)
    - `location_lng` number, nullable: Location longitude (decimal degrees)
    - `remote` string, nullable: Remote work policy
    - `contract_type` string, nullable: Contract type: freelance, cdi, or both
    - `salary_min` number, nullable: Minimum salary offered (annual, in local currency)
    - `salary_max` number, nullable: Maximum salary offered (annual, in local currency)
    - `salary_currency` string, nullable: Currency code for salary (e.g., EUR, USD)
    - `salary_period` string, nullable: 'daily' when the offer pays a day rate (daily_rate_min/max); annual otherwise
    - `daily_rate_min` number, nullable: Day rate, low end
    - `daily_rate_max` number, nullable: Day rate, high end
    - `industry` string, nullable: Industry/domain (e.g., fintech, healthtech, ecommerce)
    - `company_size` string, nullable: Company size category
  - `options` MatchingOptions: Shared options for match requests (match-offers and match-candidates).
    - `min_score_threshold` number: Minimum score to include in results
    - `max_results` integer, nullable: Maximum number of results to return
    - `include_analysis` boolean: Include detailed analysis in response
    - `lang` string, nullable: Target language code for analysis translation (e.g., 'fr', 'es', 'de', 'zh'). If not specified, analysis is returned in English.
    - `page_size` integer: Number of items per page for pagination (5-100). If total items <= page_size, deep analysis runs on all items directly.
    - `location_scope` string, nullable: Location scope: 'local' (within max_distance_km), 'national' (same country), 'international' (all)
    - `max_distance_km` number, nullable: Maximum distance in km for 'local' scope (only used when location_scope='local')
    - `work_type_filter` string[], nullable: Work type filter: list of 'remote', 'hybrid', 'onsite'. Empty/None = all types
    - `role_filter` string[], nullable: RoleType enum values to prioritize in scoring (same vocabulary as candidate.interested_roles, incl. PRODUCT_DESIGNER and BUSINESS_DEVELOPER). Offers matching none of the known values get a reduced score. Unknown values are ignored; a filter made only of unknown values applies no penalty.
    - `strict_location` boolean: If true, hard-reject items outside location constraints instead of soft-penalizing
    - `contract_type_filter` string[], nullable: Contract type filter: list of 'freelance', 'cdi'. Empty/None = all types
    - `ab_variant` string, nullable: Experiment variant assigned upstream by GetYourJob (typical values: 'control', 'treatment'). The server accepts any string for forward-compat and echoes it back in the response metadata. It DOES NOT branch matching behavior on this value — it's logged for offline analysis only.

## Response `200`

Successful batch match results

- unknown

## Other responses

- `400`: Validation error
- `401`: Authentication required
- `422`: Validation Error
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/getyourjob/apis/matchingagent-api.md) · [All operations](https://skmtc.dev/getyourjob/apis/matchingagent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getyourjob/apis/matchingagent-api/revisions/3a2d9ceb7c84?raw)
