Using GraphQL
The Udacity Public API uses GraphQL. This guide covers the Udacity-specific patterns you need to know — if you’re already familiar with GraphQL, skip straight to the sections that matter.
Multi-endpoint architecture
Unlike a single-graph API, Udacity exposes a separate GraphQL endpoint per domain. Each endpoint has its own schema and handles one area of data:
| Endpoint | What it returns |
|---|---|
https://api.udacity.com/api/public/api/v1/catalog/graphql | Programs, assessments, and learning plans in your company catalog |
https://api.udacity.com/api/public/api/v1/program-progress/graphql | Enrollment status and completion progress per learner per program |
https://api.udacity.com/api/public/api/v1/assessment-progress/graphql | Assessment attempt results per learner |
https://api.udacity.com/api/public/api/v1/learning-plan-progress/graphql | Learner progress through learning plan steps |
https://api.udacity.com/api/public/api/v1/tokens/graphql | Create, revoke, and manage API tokens (JWT auth, not token auth) |
In practice this means: to build a full learner dashboard, you make separate requests to the catalog endpoint (what’s available), the program progress endpoint (how learners are doing), and optionally the assessment progress endpoint (assessment results). You cannot query across domains in a single request.
Making requests
Every request is a POST with a JSON body containing your query:
curl -X POST https://api.udacity.com/api/public/api/v1/catalog/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Token YOUR_API_KEY" \
-d '{
"query": "query GetCatalog($contractId: ID) { catalog(contractId: $contractId) { companyId programs { key title } } }",
"variables": { "contractId": "optional-contract-id" }
}'| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | The GraphQL query or mutation |
variables | object | No | Values for any variables declared in the query |
operationName | string | No | Which operation to run (only needed if the query string contains multiple operations) |
The input pattern
All progress queries use a structured input object that combines filtering, sorting, and pagination into a single argument. Once you understand this pattern, every progress endpoint works the same way:
query GetProgramProgress($input: ProgramProgressInput!) {
programProgress(input: $input) {
totalCount
pageInfo { hasNextPage endCursor }
edges {
node {
userEmail
enrollmentStatus
completionPercentage
}
}
}
}Variables:
{
"input": {
"filter": {
"enrollmentStatus": ["ENROLLED"],
"programType": ["NANODEGREE"]
},
"orderBy": "LAST_ACTIVE",
"order": "DESC",
"first": 50,
"after": null
}
}The input object always supports the same structure:
| Field | Purpose |
|---|---|
filter | Object with domain-specific filter fields (varies by endpoint) |
orderBy | Enum specifying which field to sort by |
order | ASC or DESC |
first | Page size (default 50) |
after | Cursor from pageInfo.endCursor for the next page |
The Catalog API is the exception — it returns the entire catalog in one response with no pagination or filtering needed.
Always use variables
Use variables instead of string interpolation for dynamic values. This prevents injection issues and makes queries reusable:
# Good — use variables
query GetCatalog($contractId: ID) {
catalog(contractId: $contractId) {
companyId
programs { key title }
}
}
# Pass separately:
# { "contractId": "your-contract-id" }# Bad — hardcoded values
query {
catalog(contractId: "abc-123") {
companyId
}
}Select only the fields you need
GraphQL returns exactly the fields you request — no more. Requesting fewer fields reduces response size and can improve performance:
# Lightweight — just keys and titles
{ catalog { programs { key title } } }
# Full detail — includes all metadata
{
catalog {
programs {
key title type summary duration
modifiedDate syllabusUrl imageUrl
ssoLaunchLinksByContract { contractId launchUrl }
}
}
}Next steps
- Make Your First Request — Step-by-step curl examples
- Pagination — Handle large result sets with cursor-based pagination
- Filtering & Sorting — Available filters for each endpoint
- Integration Examples — Complete Python and Node.js scripts