API documentation

Use Facultas to return ranked, explained opportunity matches.

The Facultas API accepts a student or candidate profile, applies hard constraints, retrieves relevant opportunities, and returns ranked matches with source links and explanation signals.

Agent-readable summary: /llms.txt. This page is static HTML and does not require client-side JavaScript.

01 Quickstart

Integration flow

  1. Request API access and store the issued key server-side.
  2. Collect the user profile, preferences, and hard constraints in your product.
  3. Send the profile to POST /v1/match.
  4. Render the returned matches with employer, title, score, explanation, and source URL.

The API is designed to sit behind your application server. Do not expose API keys in browser JavaScript, mobile apps, or public repositories.

02 Access and auth

Base URL and headers

Production API access is currently issued to approved partners. Confirm your final base URL during onboarding; examples use the public production host.

The partner API and member browser API use different bearer credentials. Partner integrations use an issued Facultas API key. The member resume library uses a short-lived Google ID token for the signed-in person and never places a partner key in browser code.

Base URL
https://api.facultas.work
Version
v1
Authentication
X-API-Key: FACULTAS_API_KEY
Content type
application/json

03 Endpoint

POST /v1/match

Submit a profile and receive ranked job or internship matches. Hard constraints are applied before ranking, so returned matches should be eligible candidates for display.

Request
curl https://api.facultas.work/v1/match \
  -H "X-API-Key: $FACULTAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "candidate_123",

    "background": {
      "field_of_study": "International Affairs",
      "degree_level": "masters",
      "years_experience": 2,
      "experience_summary": "Graduate student in international affairs with quantitative research experience.",
      "languages": ["English", "Spanish"],
      "work_authorized_countries": ["USA"]
    },

    "interests": ["climate policy", "public sector"],
    "hard_skills": ["Python", "GIS", "policy analysis"],

    "hard_constraints": {
      "job_types": ["internship"],
      "excluded_sectors": ["defense"],
      "availability_start": "2026-06-01",
      "needs_visa_sponsorship": false
    },

    "preferences": {
      "preferred_locations": ["Washington", "Remote"],
      "preferred_sectors": ["Public/Government"]
    },

    "options": {
      "limit": 10,
      "include_explanations": true
    }
  }'

04 Request schema

Request fields

Unknown fields are ignored, not rejected. A request using the wrong shape returns 200 OK with results that silently disregard what you sent. If matches look generic, check your payload against the field names below first — in particular hard_skills and interests are top level, and limit lives under options.

FieldTypeRequiredDescription
profile_id string Yes Your stable identifier for the user or session. Opaque — never send names, emails, or other personal identifiers.
background.field_of_study string No Primary academic or professional field. A ranking signal, never a filter.
background.degree_level string No high school, associate, bachelors, masters, phd.
background.years_experience number No Years of professional experience. Drives the experience-gap demotion — supply it if you have it.
background.experience_summary string No Free text. Contributes heavily to semantic matching, so a richer summary meaningfully improves results.
background.languages array<string> No Spoken languages, English names or ISO 639-1 codes.
background.work_authorized_countries array<string> No Turns on the work-authorization gate when supplied.
background.is_us_citizen boolean No Only used with options.require_eligible, and only to drop postings with a confirmed citizenship requirement.
background.clearance_status string No active, inactive, none, or unknown. Supplying it activates clearance handling.
interests array<string> No Sectors, issue areas, or mission preferences. Top level — not nested under background.
hard_skills array<string> No Tools, methods, certifications. Top level. Ubiquitous office software is ignored — see Prioritizing constraints.
hard_constraints.* object No Deal-breakers. Applied before ranking. See Prioritizing constraints.
preferences.* object No Soft signals — preferred_locations, preferred_sectors, minimum_compensation. Influence rank, never exclude.
options.limit integer No Matches to return. Default 20, max 100. Nested under options, not top level.
options.include_explanations boolean No Default true. Returns a human-readable reason per match.
options.exclude_seen boolean No Default true. Suppresses postings already served to this profile_id.

05 Response shape

Ranked matches with explanations

Each match includes a normalized opportunity, canonical source URL, score, and explanation signals that your product can show directly or adapt for its own UI.

Response
{
  "request_id": "req_01hx7m8k9p",
  "matches": [
    {
      "opportunity_id": "opp_wri_4271",
      "title": "Climate Data Intern",
      "employer": "World Resources Institute",
      "location": "Washington, DC",
      "employment_type": "internship",
      "source_url": "https://example.org/jobs/climate-data-intern",
      "score": 0.87,
      "explanation": {
        "summary": "Strong climate-policy fit with GIS and Python requirements.",
        "criteria": [
          { "name": "skills", "score": 0.91, "evidence": ["Python", "GIS"] },
          { "name": "field_fit", "score": 0.86, "evidence": ["climate policy"] },
          { "name": "placement_prior", "score": 0.78, "evidence": ["6 prior placements"] }
        ]
      }
    }
  ],
  "meta": {
    "limit": 10,
    "returned": 1
  }
}

06 Ranking behavior

How Facultas decides what to return

Hard constraints first

Work authorization, start dates, location requirements, exclusions, and similar gates are applied before ranking.

Hybrid retrieval

Semantic retrieval captures meaning while keyword retrieval preserves exact signals such as tools, certifications, and agency names.

Placement prior

Historical outcomes help rank employers that have actually hired from the relevant population.

Traceable output

Returned opportunities include source URLs and explanation criteria so a user can inspect why each match appeared.

07 Prioritizing constraints

Deal-breakers, gates, and preferences

Facultas applies candidate requirements in three tiers. Choosing the right tier matters more than any other request decision: put something too high and you get an empty page, too low and it barely registers.

Tier 1 — Hard constraints (hard_constraints)

Deal-breakers. Applied in SQL before retrieval, so an excluded posting can never appear at any rank. Use these only for things that make a posting genuinely unusable.

FieldDefaultEffect when set
job_typesunsetKeeps only these types (internship, fulltime, parttime, fellowship, contract, temporary).
allowed_countriesunsetKeeps only these countries. Remote roles bypass this unless remote_ok is false.
remote_oktrueSet false to exclude remote roles entirely.
excluded_sectorsunsetDrops these sectors. Postings with no recorded sector still pass.
paid_onlyfalseDrops confirmed unpaid postings. Off by default — unpaid internships and fellowships stay servable unless you ask otherwise. Unknown pay is always kept.
needs_visa_sponsorshipfalseDrops postings that explicitly refuse sponsorship.
availability_start / availability_endunsetRequires the posting window to overlap yours. Open-ended postings pass.

Tier 2 — Eligibility gates (options.require_*)

Opt-in gates evaluated after retrieval and before ranking. Each produces a reasoned verdict — eligible, ineligible, unknown, or not_applicable — returned per match in eligibility_evaluation. Only a confirmed ineligible removes a posting. Every one of these defaults to off.

OptionGates onTurn it on when
require_clearance_matchSecurity clearance levelYour users cannot obtain clearances. Otherwise cleared roles are demoted but still shown.
require_work_authorization_matchPosting country vs authorized countriesAlso switches on automatically when work_authorized_countries is supplied.
require_eligibleConfirmed citizenship requirementsYour users are non-citizens. Only drops postings with a structured requirement.
require_min_compensationpreferences.minimum_compensationPay below the floor is unusable. Unknown pay is kept.
require_degree_matchPosting degree levelRarely. It compares level only, never field of study.
require_experience_matchMinimum yearsRarely. This is a hard drop. Experience is already demoted smoothly — see below.
require_language_matchPosting languagesThe role is unperformable without the language.

Prefer demotion to exclusion. Experience is the clearest example: a candidate with 2 years is short of roughly a third of postings, so require_experience_match would delete a third of the pool. Leaving it off keeps the automatic gap-based demotion, which grows as the gap widens — a one- or two-year stretch is barely penalised, while a role wanting five more years than the candidate has drops far down. Reach for a gate only when a mismatched posting is useless rather than merely ambitious.

Tier 3 — Preferences (preferences)

Soft signals. They move a posting up or down, never remove it. preferred_locations, preferred_sectors, and minimum_compensation (which stays soft unless you set require_min_compensation).

Location and sector currently score as matched or not matched rather than on a gradient, so they carry more weight than their nominal share suggests. Listing many preferred locations dilutes the signal; listing one or two concentrates it. If your results skew heavily toward a single employer or city, over-specified preferences are the first thing to check.

How unknown data is treated

Missing information is never read as a mismatch. A posting that does not state a requirement produces unknown, and the default policy allows it through — the reasoning being that our extraction gaps should not cost a candidate an opportunity. The single exception is job_type, which blocks on unknown, because a request for internships should not return roles of indeterminate type. You can override this per constraint with options.eligibility_unknown_policy, for example {"security_clearance": "block"}.

Practical ordering

  1. Start with only genuine deal-breakers in hard_constraints — usually just job_types.
  2. Supply background facts (years_experience, languages, work_authorized_countries, clearance_status). Several gates activate on their own once the facts are present.
  3. Express everything else as preferences and let ranking sort it out.
  4. Add require_* gates only after seeing results that are genuinely unusable rather than simply imperfect.
  5. Inspect score_breakdown and eligibility_evaluation on real responses to see which signals actually moved.

08 Member resume API

Independent resume variants for authenticated members

These endpoints back the Facultas member resume library. They accept Authorization: Bearer GOOGLE_ID_TOKEN; the local development bypass may use X-Dev-Email only when both the page and API are running directly on loopback and the backend bypass flag is enabled.

EndpointPurpose
GET /v1/member/resumesList the signed-in member's safe resume metadata.
POST /v1/member/resumesUpload one or more independent PDF or DOCX files.
PATCH /v1/member/resumes/{public_id}Rename, change archetype, set default, archive, or restore an owned resume.
DELETE /v1/member/resumes/{public_id}Safely delete an owned resume under the retention policy.
GET /v1/member/matching-settingsRead account-level eligibility and search constraints.
PUT /v1/member/matching-settingsReplace structured eligibility, hard_constraints, preferences, and options with explicit member choices.
POST /v1/member/matchesMatch with exactly one named resume or aggregate across all ready resumes.

Batch upload

Send repeated files multipart fields and an optional metadata JSON array in the same order. Each metadata item may contain a label of up to 80 characters and a non-null archetype such as general, policy, or data. The response reports each file independently, so accepted files remain accepted when a sibling fails.

Upload
curl https://api.facultas.work/v1/member/resumes \
  -H "Authorization: Bearer $GOOGLE_ID_TOKEN" \
  -F "[email protected]" \
  -F "[email protected]" \
  -F 'metadata=[{"label":"Policy","archetype":"policy"},{"label":"Data","archetype":"data"}]'

List and upload responses expose only safe metadata: public id, label, archetype, default state, parse status and safe parse_error, original filename, content type, parser/schema versions, timestamps, and archive state. Storage paths and internal database identifiers are never part of the browser contract.

Single and all-resume matching

Requests
{ "mode": "single", "resume_id": "7b55d3b7-...", "limit": 10 }

{ "mode": "all", "limit": 10 }

Single mode requires one owned, ready resume and never merges another variant. All mode evaluates ready variants, deduplicates opportunities, and annotates each job with best_resume and optional alternate_resume_fits. The response also includes mode, request_id, match provenance, normal match fields, and metadata.

Account settings and explicit request overrides are authoritative for visa, citizenship, work authorization, clearance, geography, paid-only, compensation, and job-type constraints. A resume contributes retrieval and ranking signals only; it cannot silently relax or replace those constraints. Per-search overrides may narrow account hard constraints, but cannot loosen them.

Account settings
{
  "eligibility": {
    "is_us_citizen": true,
    "citizenships": ["United States"],
    "work_authorized_countries": ["United States"],
    "needs_visa_sponsorship": false,
    "clearance_status": "active",
    "clearance_level": "Public Trust"
  },
  "hard_constraints": {
    "job_types": ["Full-time"],
    "allowed_countries": ["United States"],
    "remote_ok": true,
    "excluded_sectors": ["Defense"],
    "needs_visa_sponsorship": false,
    "paid_only": true,
    "availability_start": "2026-08-01"
  },
  "preferences": {
    "preferred_locations": ["Washington DC"],
    "preferred_sectors": ["Climate"],
    "minimum_compensation": 65000
  },
  "options": {}
}

minimum_compensation is annual USD. The current engine does not accept a currency or pay-period field.

09 Errors

Error format and status codes

Error
{
  "detail": [
    {
      "type": "int_parsing",
      "loc": ["body", "options", "limit"],
      "msg": "Input should be a valid integer",
      "input": "ten"
    }
  ]
}
Status Meaning Client action
422 Body failed validation. detail[].loc names the offending field path. Fix the field named in loc. Note that unknown fields do not 422 — they are ignored.
401 Missing or invalid API key. Check the bearer token.
422 Valid JSON, but required profile fields are missing. Prompt for the missing fields or retry with a complete profile.
429 Rate limit exceeded. Back off and retry after the returned limit window.
500 Unexpected server error. Retry with exponential backoff and include request_id in support requests.

10 Agent guidance

How agents should use the API

  • Use /llms.txt for a compact machine-readable summary, then use this page for full field definitions.
  • Send only profile data needed for matching. Avoid names, emails, resumes, transcripts, and private notes unless explicitly required by a partner integration.
  • Treat constraints as hard filters, not ranking preferences.
  • Show source_url whenever presenting an opportunity so users can verify the live posting.
  • Present score and explanation.summary as matching evidence, not as a guarantee of hiring or eligibility.
  • When an API response contains no matches, ask the user which constraints to relax before retrying.

Need production access?

Tell us about your platform, expected volume, and the profiles you need to match.

Request API access