Make Your First Request
Walk through obtaining a token and querying the Udacity Public API — starting with the Catalog, then adding learner progress data.
Prerequisites
- A Client ID and Client Secret — see Generate API Credentials
- A way to make HTTP requests:
curl, Postman, or the interactive Playground
All Udacity API endpoints accept POST requests with a JSON body containing a query field. You only receive the fields you request.
https://api.udacity.com/api/public/apiGet a token
Exchange your credentials for a short-lived bearer token. The Tokens API is the only endpoint that doesn’t require an Authorization header.
Your bearer token. Use as YOUR_TOKEN in all subsequent requests. Store it securely — it grants full API access.
Token lifetime — 3600 by default (1 hour). Refresh before it expires.
curl -X POST https://api.udacity.com/api/public/api/v1/tokens/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($input: GenerateTokenInput!) { generateToken(input: $input) { unredactedToken expiresIn } }",
"variables": {
"input": {
"clientId": "udcl_YOUR_CLIENT_ID",
"clientSecret": "udcs_YOUR_CLIENT_SECRET"
}
}
}'{
"data": {
"generateToken": {
"unredactedToken": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"expiresIn": 3600
}
}
}List your programs
With a token, make your first data request. Pass it in the Authorization header — note the scheme is Token, not Bearer.
GraphQL returns exactly the fields you ask for. Start narrow and expand your selection as needed.
curl -X POST https://api.udacity.com/api/public/api/v1/catalog/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Token YOUR_TOKEN" \
-d '{"query": "{ catalog { companyId programs { key title } } }"}'{
"data": {
"catalog": {
"companyId": "4821",
"programs": [
{ "key": "nd00113", "title": "Cloud Architecture" },
{ "key": "cd12345", "title": "Intro to Cloud Computing" }
]
}
}
}Get the full catalog
The Catalog API returns programs, assessments, and learning plans in a single query. Expand your field selection to pull everything at once.
Only request fields you need — lean queries keep payloads small and response times fast, even for large catalogs.
curl -X POST https://api.udacity.com/api/public/api/v1/catalog/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Token YOUR_TOKEN" \
-d '{
"query": "{ catalog { companyId programs { key title type } assessments { id title category } learningPlans { id title key } } }"
}'{
"data": {
"catalog": {
"companyId": "4821",
"programs": [
{ "key": "nd00113", "title": "Cloud Architecture", "type": "NANODEGREE" }
],
"assessments": [
{ "id": "87fac66e", "title": "Python Fundamentals", "category": "PASS_FAIL" }
],
"learningPlans": [
{ "id": "fd6f0490", "title": "Cloud Engineering Onboarding", "key": "cloud-eng" }
]
}
}
}Query learner progress
The Program Progress API returns enrollment and completion data for all learners in your organization. Progress queries use cursor-based pagination via an input object.
When true, pass pageInfo.endCursor as the after argument in your next call. See Pagination.
Progress from 0.0 to 100.0 across all program content.
curl -X POST https://api.udacity.com/api/public/api/v1/program-progress/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Token YOUR_TOKEN" \
-d '{
"query": "{ programProgress(input: { first: 5 }) { totalCount pageInfo { hasNextPage endCursor } edges { node { userEmail enrollmentStatus programKey completionPercentage } } } }"
}'{
"data": {
"programProgress": {
"totalCount": 342,
"pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6IjUifQ==" },
"edges": [
{
"node": {
"userEmail": "j.martinez@acmecorp.com",
"enrollmentStatus": "ENROLLED",
"programKey": "nd00113",
"completionPercentage": 65.0
}
}
]
}
}
}Response shape & errors
Every Udacity GraphQL response uses the same envelope: data with your result and an optional errors array.
- Pagination — cursor-based paging
- Filtering & Sorting
- Error Handling
- API Reference — full schema
// Every response:
{ "data": { ... } }
// With errors:
{
"data": null,
"errors": [{
"message": "Not authorized",
"extensions": { "code": "UNAUTHORIZED" }
}]
}