MatchingAgent API Documentation

Welcome to the MatchingAgent API documentation. This guide will help you integrate our AI-powered candidate matching service into your application.

Base URL

https://api.matchingagent.ai

All API requests should be made to this base URL.

Quick Start

Get started with the MatchingAgent API in 3 simple steps:

  1. Sign up and get your API key

    Create an account at matchingagent.ai and generate an API key from your dashboard.

  2. Make your first request

    Use your API key to match multiple candidates against a job offer with /v1/match-candidates:

    curl -X POST https://api.matchingagent.ai/v1/match-candidates \
      -H "X-API-Key: ma_sk_live_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "offer": {
          "external_id": "job-456",
          "title": "Full-Stack Developer",
          "company": "TechCorp",
          "required_skills": ["React", "Node.js", "TypeScript"],
          "seniority_required": "senior"
        },
        "candidates": [
          {
            "external_id": "candidate-001",
            "name": "Alice Martin",
            "experience_years": 6,
            "seniority": "senior",
            "skills": ["React", "Node.js", "TypeScript", "PostgreSQL"]
          },
          {
            "external_id": "candidate-002", 
            "name": "Bob Johnson",
            "experience_years": 3,
            "seniority": "mid",
            "skills": ["React", "Python", "Django"]
          }
        ]
      }'
  3. Process the results

    The API returns scored matches with detailed breakdowns, recommendations, and automatic pagination for large batches.

3 API Endpoints

POST
/v1/match-candidates

Match up to 5,000 candidates against 1 offer (auto-paginated)

POST
/v1/match-offers

Match 1 candidate against up to 100 offers

POST
/v1/matching-session

Get paginated results from large matching requests

Authentication

All API requests require authentication using an API key. Include your API key in the request header:

X-API-Key: ma_sk_live_your_key_here
Keep your API key secure
Never expose your API key in client-side code or public repositories. Use environment variables and server-side requests only.

Rate Limits

Rate limits vary by plan tier:

PlanRequests/MinuteMonthly Quota
Pay-as-you-go100Unlimited (billed per match)
Starter50015,000 matches
Professional2,000100,000 matches
Enterprise10,000500,000 matches

Usage Quotas

Each plan includes a monthly quota of matches. When you reach 80% of your quota, you'll receive an email warning. When you exceed 100%, API requests will be blocked with a 402 Payment Required response.

Enable Pay-as-you-go for overages
You can enable pay-as-you-go billing to continue using the API beyond your quota at €0.001 per match. Configure this in your billing settings.

Match Candidates Endpoint

Match multiple candidates against a single job offer. This is the primary endpoint for high-volume candidate screening and supports up to 5,000 candidates with automatic pagination.

POST /v1/match-candidates

Request Body

{
  "offer": {
    "external_id": "string",
    "title": "string",
    "company": "string",
    "required_skills": ["string"],
    "nice_to_have_skills": ["string"],
    "seniority_required": "junior" | "mid" | "senior" | "lead",
    "experience_years_min": number,
    "experience_years_max": number
  },
  "candidates": [{
    "external_id": "string",
    "name": "string",
    "experience_years": number,
    "seniority": "junior" | "mid" | "senior" | "lead",
    "skills": ["string"],
    "location": "string"
  }],
  "output_language": "en" | "fr" (optional)
}
Automatic pagination for large batches
When you send more than 100 candidates, results are automatically paginated. Use the matching_session_id returned to fetch additional pages.

Response

{
  "success": true,
  "request_id": "ma-req-uuid",
  "offer_external_id": "job-456",
  "matching_session_id": "ms-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "pagination": {
    "current_page": 1,
    "total_pages": 5,
    "total_items": 450,
    "items_per_page": 100,
    "has_next": true
  },
  "matches": [{
    "candidate_external_id": "candidate-001",
    "candidate_name": "Alice Martin",
    "score": {
      "overall": 9.2,
      "confidence": 0.95,
      "grade": "A"
    },
    "breakdown": {
      "skills": {
        "score": 9.5,
        "weight": 0.40,
        "details": "3/3 required skills matched + 1 bonus skill"
      },
      "experience": {
        "score": 9.0,
        "weight": 0.35,
        "details": "6 years vs 4-8 year range"
      },
      "seniority": {
        "score": 10.0,
        "weight": 0.25,
        "details": "Exact match: senior = senior"
      }
    },
    "analysis": {
      "pros": ["All required skills present", "Strong experience match", "Bonus PostgreSQL skill"],
      "cons": [],
      "recommendation": "STRONGLY_RECOMMEND"
    }
  }],
  "metadata": {
    "processing_time_ms": 1850,
    "candidates_evaluated": 100
  }
}

Match Offers Endpoint

Match a single candidate against multiple job offers (up to 100). Useful for candidate-centric workflows where you need to find the best job matches for a candidate.

POST /v1/match-offers

Request Body

{
  "candidate": {
    "external_id": "string",
    "name": "string",
    "experience_years": number,
    "seniority": "junior" | "mid" | "senior" | "lead",
    "skills": ["string"],
    "education": {
      "degree": "string",
      "field": "string"
    },
    "location": "string"
  },
  "offers": [{
    "external_id": "string",
    "title": "string",
    "company": "string",
    "required_skills": ["string"],
    "nice_to_have_skills": ["string"],
    "seniority_required": "junior" | "mid" | "senior" | "lead",
    "experience_years_min": number,
    "experience_years_max": number
  }],
  "output_language": "en" | "fr" (optional)
}

Matching Session Endpoint

Retrieve paginated results from a large matching request. Use the session_id returned from /v1/match-candidates.

POST /v1/matching-session

Request Body

{
  "session_id": "ms-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "page": 2,
  "include_analysis": true,
  "lang": "en" (optional)
}

Response

{
  "success": true,
  "session_id": "ms-a1b2c3d4-...",
  "page": 2,
  "pagination": {
    "current_page": 2,
    "total_pages": 5,
    "total_items": 450,
    "items_per_page": 100,
    "has_next": true,
    "has_previous": true
  },
  "matches": [...],
  "from_cache": false,
  "processing_time_ms": 8500
}

Pagination for Large Requests

When matching more than 100 candidates against an offer, the API automatically creates a matching session and returns paginated results. Use the matching_session_id to fetch additional pages of results.

# Step 1: Initial request with 500 candidates
response = requests.post("/v1/match-candidates", json={
    "offer": {...},
    "candidates": [... 500 candidates ...]
})

# Response includes session_id and first 100 results
session_id = response.json()["matching_session_id"]
first_100 = response.json()["matches"]

# Step 2: Get page 2 (candidates 101-200)
page2 = requests.post("/v1/matching-session", json={
    "session_id": session_id,
    "page": 2
})

# Step 3: Continue for remaining pages...

Response Format

{
  "success": true,
  "request_id": "ma-req-uuid",
  "offer_external_id": "job-456",
  "matches": [{
    "candidate_external_id": "candidate-123",
    "candidate_name": "John Doe",
    "score": {
      "overall": 8.7,
      "confidence": 0.92,
      "grade": "A-"
    },
    "breakdown": {
      "skills": {
        "score": 9.0,
        "weight": 0.40,
        "details": "2/2 required skills matched"
      },
      "experience": {
        "score": 8.5,
        "weight": 0.35,
        "details": "5 years vs 3-7 year range"
      },
      "seniority": {
        "score": 10.0,
        "weight": 0.25,
        "details": "Exact match: senior = senior"
      }
    },
    "analysis": {
      "pros": ["All required skills present"],
      "cons": [],
      "recommendation": "STRONGLY_RECOMMEND"
    }
  }],
  "metadata": {
    "processing_time_ms": 1250,
    "candidates_evaluated": 1
  }
}

Python Examples

Match Candidates (Recommended)

Match multiple candidates against a single job offer with automatic pagination:

import requests

api_key = "ma_sk_live_your_key_here"
base_url = "https://api.matchingagent.ai"
headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

# Match multiple candidates against one offer (recommended approach)
payload = {
    "offer": {
        "external_id": "job-456",
        "title": "Full-Stack Developer",
        "company": "TechCorp",
        "required_skills": ["React", "Node.js", "TypeScript"],
        "seniority_required": "senior"
    },
    "candidates": [
        {
            "external_id": f"cand-{i}",
            "name": f"Candidate {i}",
            "skills": ["React", "Node.js", "TypeScript"],
            "experience_years": 5,
            "seniority": "senior"
        }
        for i in range(500)  # 500 candidates
    ]
}

response = requests.post(f"{base_url}/v1/match-candidates", headers=headers, json=payload)
data = response.json()

# First 100 results are returned immediately, sorted by score
print(f"Got {len(data['matches'])} matches on page 1")
print(f"Top candidate: {data['matches'][0]['candidate_name']} - Score: {data['matches'][0]['score']['overall']}")

# Use session_id to get more pages
if "matching_session_id" in data:
    session_id = data["matching_session_id"]
    total_pages = data["pagination"]["total_pages"]
    
    # Fetch all remaining pages
    for page in range(2, total_pages + 1):
        page_response = requests.post(
            f"{base_url}/v1/matching-session",
            headers=headers,
            json={"session_id": session_id, "page": page}
        )
        page_data = page_response.json()
        print(f"Got {len(page_data['matches'])} matches on page {page}")

Match Offers (Alternative)

Match a single candidate against multiple job offers:

import requests

api_key = "ma_sk_live_your_key_here"
base_url = "https://api.matchingagent.ai"
headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

# Match one candidate against multiple offers
payload = {
    "candidate": {
        "external_id": "candidate-123",
        "name": "John Doe",
        "experience_years": 5,
        "seniority": "senior",
        "skills": ["React", "Node.js", "TypeScript"]
    },
    "offers": [{
        "external_id": "job-456",
        "title": "Full-Stack Developer",
        "required_skills": ["React", "Node.js"],
        "seniority_required": "senior"
    }]
}

response = requests.post(f"{base_url}/v1/match-offers", headers=headers, json=payload)

if response.status_code == 200:
    data = response.json()
    for match in data["matches"]:
        print(f"Match: {match['offer_title']}")
        print(f"Score: {match['score']['overall']}/10")
        print(f"Recommendation: {match['analysis']['recommendation']}")
elif response.status_code == 402:
    data = response.json()
    print(f"Quota exceeded: {data['used']}/{data['quota']}")
else:
    print(f"Error: {response.status_code}")

Node.js Example

Match Candidates with Pagination

const apiKey = "ma_sk_live_your_key_here";
const baseUrl = "https://api.matchingagent.ai";

const headers = {
  "X-API-Key": apiKey,
  "Content-Type": "application/json"
};

// Generate 500 test candidates
const candidates = Array.from({ length: 500 }, (_, i) => ({
  external_id: `cand-${i}`,
  name: `Candidate ${i}`,
  skills: ["React", "Node.js", "TypeScript"],
  experience_years: Math.floor(Math.random() * 10) + 1,
  seniority: ["junior", "mid", "senior"][Math.floor(Math.random() * 3)]
}));

// Match candidates against one offer (recommended)
const payload = {
  offer: {
    external_id: "job-456",
    title: "Full-Stack Developer",
    company: "TechCorp",
    required_skills: ["React", "Node.js", "TypeScript"],
    seniority_required: "senior"
  },
  candidates
};

const response = await fetch(`${baseUrl}/v1/match-candidates`, {
  method: "POST",
  headers,
  body: JSON.stringify(payload)
});

if (response.ok) {
  const data = await response.json();
  
  console.log(`Page 1: ${data.matches.length} matches`);
  console.log(`Top candidate: ${data.matches[0].candidate_name}`);
  console.log(`Score: ${data.matches[0].score.overall}/10`);
  
  // Fetch remaining pages if needed
  if (data.matching_session_id) {
    const sessionId = data.matching_session_id;
    const totalPages = data.pagination.total_pages;
    
    for (let page = 2; page <= totalPages; page++) {
      const pageRes = await fetch(`${baseUrl}/v1/matching-session`, {
        method: "POST",
        headers,
        body: JSON.stringify({ session_id: sessionId, page })
      });
      const pageData = await pageRes.json();
      console.log(`Page ${page}: ${pageData.matches.length} matches`);
    }
  }
} else if (response.status === 402) {
  const data = await response.json();
  console.log(`Quota exceeded: ${data.used}/${data.quota}`);
}

Error Handling

The API uses standard HTTP response codes:

CodeMeaning
200Success - Request completed successfully
400Bad Request - Invalid parameters or malformed JSON
401Unauthorized - Invalid or missing API key
402Payment Required - Monthly quota exceeded. Upgrade your plan or enable pay-as-you-go.
404Not Found - Session not found or expired (for matching-session endpoint)
429Too Many Requests - Rate limit exceeded
500Internal Server Error - Something went wrong on our end

Quota Exceeded Response (402)

When your monthly quota is exceeded, the API returns:

{
  "error": "quota_exceeded",
  "message": "Monthly quota exceeded. Please upgrade your plan or enable pay-as-you-go.",
  "quota": 15000,
  "used": 15234,
  "plan_tier": "starter",
  "upgrade_url": "https://matchingagent.com/billing/upgrade",
  "billing_url": "https://matchingagent.com/billing",
  "pay_as_you_go_available": true,
  "period_end": "2026-02-01T00:00:00Z"
}

Need Help?

Can't find what you're looking for? We're here to help!