FETU Developer Hub · API v1

Build with FETU.

Integrate engineering jobs, internships, scholarships and other FETU-reviewed opportunities into your website or application — with a free, read-only public API and a copy-and-paste widget.

bash
curl "https://www.fetu-uganda.org/api/v1/opportunities?type=internship&limit=3"

Public REST API

Versioned JSON endpoints for published opportunities and categories. No API key required.

Embeddable widget

An isolated iframe widget with type, discipline, location, theme and size options.

Partner integrations

Universities, employers and associations can display FETU opportunities and submit their own.

Overview

Base URL https://www.fetu-uganda.org/api/v1. All endpoints are public, read-only GET requests returning JSON. Only opportunities approved by FETU staff are ever returned; pending, rejected and archived records and all submitter details are never exposed.

GET/api/v1/healthService status and database connectivity check. Not cached.
GET/api/v1/categoriesOpportunity types and engineering disciplines with active published counts.
GET/api/v1/opportunitiesPaginated list of published, non-expired opportunities with search and filters.
GET/api/v1/opportunities/:idA single published opportunity by UUID or slug. Includes deadline_status.

Query parameters — GET /opportunities

ParameterValuesDescription
qstring ≤100Keyword search across title, organization, summary and location.
typejob | internship | graduate_trainee | scholarship | fellowship | apprenticeship | competition | professional_development | otherOpportunity type.
disciplinecivil | mechanical | electrical | electronics_telecom | chemical | software_it | agricultural | biomedical | environmental | mining_petroleum | building_construction | multidisciplinaryEngineering discipline.
locationstring ≤80Partial match on location, e.g. Kampala.
pageinteger 1–1000, default 1Page number.
limitinteger 1–50, default 12Results per page.
sortrecent | deadline, default recentrecent = newest published first; deadline = soonest deadline first.
include_expiredtrue | false, default falseInclude published opportunities whose deadline has passed.

Unknown parameters are ignored; a status parameter is ignored — only published records are served. Invalid values return 400.

200 response

json
{
  "api_version": "v1",
  "data": [
    {
      "id": "uuid",
      "slug": "graduate-trainee-engineer-a1b2c3",
      "title": "string",
      "opportunity_type": "graduate_trainee",
      "discipline": "electrical",
      "organization_name": "string",
      "location": "Kampala, Uganda" | null,
      "is_remote": false,
      "summary": "string",
      "application_url": "https://..." | null,
      "deadline": "2026-11-30T20:59:59+00:00" | null,
      "published_at": "ISO-8601 timestamp",
      "verified_at": "ISO-8601 timestamp" | null
    }
  ],
  "pagination": { "page": 1, "limit": 12, "total": 0, "total_pages": 1 }
}

Error response

json
{
  "api_version": "v1",
  "error": { "code": "invalid_parameter", "message": "limit: Number must be less than or equal to 50" }
}
  • 400 invalid_parameter · 404 not_found · 429 rate_limited · 503 service_unavailable
  • Deadlines are UTC ISO-8601 timestamps. Detail responses add deadline_status: open, closing_soon (≤7 days), expired, no_deadline.
  • Caching: list, detail and categories send Cache-Control: public, max-age=60, s-maxage=300; health is no-store.
  • CORS: Access-Control-Allow-Origin: * for GET and OPTIONS. CORS is not authentication.
  • Rate limits: about 120 requests per minute per IP, reported via X-RateLimit-* headers. Enforcement is best-effort per server instance, so exact limits may vary.