JOIN APIjoin.com ↗
Retrieve the full application form for any join.com job posting: personal info fields, document slots, and screening questions with types, options, and required flags.
What is the JOIN API?
The JOIN.com Application Form API exposes the complete public application form for any join.com job posting in a single call via the get_application_form endpoint. One request returns over 6 structured response objects — including job metadata, document upload requirements, standard personal-information fields, default qualification questions, and all job-specific screening questions with their types, answer options, and required flags — identified by the company slug and numeric job ID from the posting URL.
curl -X GET 'https://api.parse.bot/scraper/268481b3-0b82-4287-9c8d-031d25646cb8/get_application_form?job_id=16664160&company_slug=productsup' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace join-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: JOIN Job Application Form API — fetch and inspect a job's application form."""
from parse_apis.join_com_api import JoinCom, InputNotFound
client = JoinCom()
# Fetch the full application form for a specific job posting.
try:
form = client.application_forms.get(company_slug="productsup", job_id="16664160")
except InputNotFound:
print("Job not found or does not belong to this company.")
raise
# Inspect the embedded job summary.
job = form.job
print(f"{job.title} at {job.company_name} ({job.workplace_type})")
print(f"Location: {job.company_location}")
# List required personal-information fields the applicant must fill in.
for field in form.standard_fields:
marker = "*" if field.is_required else " "
print(f" [{marker}] {field.label} ({field.type})")
# Show which documents are needed.
for doc in form.documents:
print(f" Document: {doc.type} — {doc.constraint}")
# Walk screening questions, noting any with selectable options.
if form.screening_questions is not None:
for sq in form.screening_questions:
required = "required" if sq.is_required else "optional"
print(f" Q{sq.position}: {sq.question} [{sq.type}, {required}]")
if sq.options is not None:
print(f" choices: {', '.join(sq.options)}")
print(f"Total screening questions reported: {form.screening_questions_total}")
print("exercised: application_forms.get")
Returns the complete application form for one JOIN job, identified by the company slug and numeric job id that appear in a join.com job URL (join.com/companies/<company_slug>/<job_id>). One call makes two fixed upstream round trips (job lookup, then the screening-question list, paged internally up to a hard cap). The payload contains: a short job summary (title, status, company, workplace type, salary frequency); the wizard's fixed personal-information fields (standard_fields, a constant list of the site's own form fields); the document uploads the company configured (documents, each REQUIRED or OPTIONAL); the company-configured default questions if any (default_questions: work authorization, start availability or salary expectation, empty when the job uses none); and the job-specific screening questions ordered by position (screening_questions), where type is one of the values observed on the site (BOOLEAN, MULTI_CHOICE, TEXT; other types exist upstream and are passed through unchanged), options carries the selectable choices for choice questions and is null otherwise. screening_questions may legitimately be empty for a job without screening questions. A job id that does not exist, or a job that does not belong to the given company slug, yields a stale_input (input_not_found) error.
| Param | Type | Description |
|---|---|---|
| job_idrequired | string | Numeric JOIN job id from the job URL, e.g. 16664160. Passed as a string of digits. |
| company_slugrequired | string | Company subdomain/slug from the job URL, e.g. the 'productsup' in join.com/companies/productsup/16664160. Must match the company that owns the job. |
{
"type": "object",
"fields": {
"job": "object: job_id, title, status (e.g. ONLINE), company_id, company_slug, company_name, workplace_type, salary_frequency, company_location",
"documents": "array of document upload slots: type (e.g. CV, COVER_LETTER), constraint (REQUIRED|OPTIONAL), is_required",
"standard_fields": "array of the wizard's fixed personal-information fields: name, label, type, is_required",
"default_questions": "array of company default questions (question_class WORK_AUTHORIZATION, AVAILABILITY or SALARY_EXPECTATION), same shape as screening_questions; empty when none",
"screening_questions": "array of job screening questions ordered by position: id, name, question, type, position, is_required, options (array of choices or null), option_values, question_class",
"screening_questions_total": "integer count of questions reported by the site (screening + default)"
},
"sample": {
"data": {
"job": {
"title": "SEO & GEO Manager (f/m/d)",
"job_id": 16664160,
"status": "ONLINE",
"company_id": 2080,
"company_name": "Productsup",
"company_slug": "productsup",
"workplace_type": "HYBRID",
"company_location": "Alex-Wedding-Straße, Berlin, Germany",
"salary_frequency": "PER_YEAR"
},
"documents": [
{
"type": "CV",
"constraint": "REQUIRED",
"is_required": true
},
{
"type": "COVER_LETTER",
"constraint": "OPTIONAL",
"is_required": false
}
],
"standard_fields": [
{
"name": "email",
"type": "EMAIL",
"label": "Email",
"is_required": true
},
{
"name": "first_name",
"type": "TEXT",
"label": "First name",
"is_required": true
},
{
"name": "last_name",
"type": "TEXT",
"label": "Last name",
"is_required": true
},
{
"name": "phone",
"type": "PHONE",
"label": "Phone number",
"is_required": false
},
{
"name": "country",
"type": "SELECT",
"label": "Country of residence",
"is_required": false
},
{
"name": "city",
"type": "SELECT",
"label": "City",
"is_required": false
},
{
"name": "professional_links",
"type": "URL_LIST",
"label": "Professional links",
"is_required": false
}
],
"default_questions": [],
"screening_questions": [
{
"id": 12252722,
"name": "Custom question",
"type": "BOOLEAN",
"options": null,
"position": 1,
"question": "Are you based in Germany?",
"is_required": true,
"option_values": null,
"question_class": null
},
{
"id": 12252724,
"name": "Custom question",
"type": "MULTI_CHOICE",
"options": [
"Master Level",
"Intermediate",
"Master level in some, intermediate in others",
"Intermediate level in some, beginner in others",
"Beginner"
],
"position": 3,
"question": "What is your level of experience with the following tools: Google Search Console, GA4, crawling software (e.g., Screaming Frog), SEO suites (Ahrefs, Semrush), and emerging AI visibility platforms.",
"is_required": true,
"option_values": null,
"question_class": null
},
{
"id": 12252725,
"name": "Custom question",
"type": "TEXT",
"options": null,
"position": 4,
"question": "How have you previously monitored and optimized for AI-driven search (GEO)? Please briefly describe your approach and the tools you used.",
"is_required": false,
"option_values": null,
"question_class": null
}
],
"screening_questions_total": 4
},
"status": "success"
}
}About the JOIN API
What the API Returns
The get_application_form endpoint accepts two required parameters: company_slug (the company identifier in the join.com URL, e.g. productsup) and job_id (the numeric job identifier, e.g. 16664160). Together these map directly to a URL of the form join.com/companies/<company_slug>/<job_id>. The response groups data into six top-level fields covering every part of the public application experience.
Job Metadata and Document Requirements
The job object returns the posting's job_id, title, status (e.g. ONLINE), company_id, company_slug, company_name, workplace_type, and salary_frequency. The documents array lists each upload slot with its type (e.g. CV, COVER_LETTER), constraint (REQUIRED or OPTIONAL), and is_required boolean, letting you understand exactly which attachments a candidate must supply.
Screening and Default Questions
The screening_questions array contains job-specific questions ordered by position, each with id, name, question text, type, is_required, and an options array for choice-based questions. The default_questions array follows the same shape but covers company-wide qualification questions with question_class values of WORK_AUTHORIZATION, AVAILABILITY, or SALARY_EXPECTATION. The screening_questions_total integer gives the site-reported combined count of screening and default questions. The standard_fields array exposes the fixed personal-information wizard fields — each with name, label, type, and is_required — giving a full picture of every field a candidate encounters before submitting.
The JOIN API is a managed, monitored endpoint for join.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when join.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official join.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Pre-fill or mirror a join.com application form in a custom ATS or careers portal using the returned
standard_fieldsandscreening_questions. - Audit hiring processes by comparing
REQUIREDvsOPTIONALdocument constraints across multiple postings. - Detect companies that require salary expectation or work authorization questions via
default_questionswithquestion_classfiltering. - Build a candidate preparation tool that displays screening questions and their answer options before a user applies.
- Track changes in job
status(e.g.ONLINE) and application requirements over time for a givenjob_id. - Aggregate screening question types across postings to analyze how join.com employers structure qualification gates.
- Validate that a job posting's document requirements match internal recruiting policy before publishing a role.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does join.com have an official developer API?+
What does `get_application_form` return for the screening questions?+
screening_questions array with each question's id, name, question text, type, position, is_required flag, and an options array listing the available answer choices for choice-based question types. A separate default_questions array covers company-level questions (work authorization, availability, salary expectation) in the same structure. The screening_questions_total field gives the combined count as reported by the posting.Can the API retrieve a list of all open jobs for a company?+
company_slug and job_id. It does not enumerate job listings for a company. You can fork the API on Parse and revise it to add a job-listing endpoint that returns all postings for a given company slug.Is the full job description text (responsibilities, requirements prose) included in the response?+
job object returns metadata fields like title, status, workplace_type, and salary_frequency, but not the free-text job description body. You can fork the API on Parse and revise it to add a field that captures the full description content from the posting page.Are there any known limitations around job availability?+
status field in the job object reflects this. Additionally, the company_slug and job_id must match exactly as they appear in the join.com URL; mismatched or malformed parameters will not resolve to a valid posting.