What changed since 0.15.16¶
For integrators on the 0.15.16-beta reference
One page covering every client-visible change from 0.15.16-beta to 0.23.0-beta, with a checklist at the end. Per-version notes are in the Changelog.
SFF Confidential
You agree to keep all Data strictly confidential and protect it with the same level of care you use to protect your own confidential information, but no less than reasonable care. Access to the Data is limited to your employees, contractors or agents who are informed of and agree to these confidentiality obligations. You are not permitted to disclose, distribute, publish, or otherwise share the Data, in any manner or medium, with any third party, without the prior written consent of the Foundation. You may not use the Data for commercial purposes.
AOI API — What Changed, 0.15.16-beta → 0.23.0-beta¶
For: integrators working from the 0.15.16-beta external REST reference (2026-08-18)
Base URL: https://aonav.ai · Date: 2026-10-26
Full reference: REST_API_REFERENCE_EXTERNAL.md (0.23.0-beta)
⚠️ Beta API — SUBJECT TO CHANGE. Fields marked SUBJECT TO CHANGE may be renamed or reshaped before 1.0. Clients should ignore unknown response keys.
Availability. Everything below through 0.22.3-beta is live on
https://aonav.ai. The 0.23.0-beta items (AI impact scores, cluster descriptions, skills summary, and the 10.23 cluster refresh) go live with the next production release; we will confirm the date.GET /api/versionreports which version you are talking to.
1. Act on these (breaking or behaviour-changing)¶
| # | Since | Change | What to do |
|---|---|---|---|
| B1 | 0.17.0 | Wages: 11 rows per MSA size, not 3. Rows are keyed on years_in (0–10). |
Read years_in (or filter ?years_in=); never index wage rows by position. experience_level "0"/"5"/"10" still return the same rows as before. |
| B2 | 0.17.0, 0.23.0 | Cluster set refreshed. 0.17.0 withdrew 82 clusters; 0.23.0 withdraws 8 and restores 23 of the 82. | Treat 404 / not-found on a previously valid cluster_id or O*NET code as normal; don't cache cluster ids indefinitely. |
| B3 | 0.16.7 | GET /api/occupations/cluster:{id}/pathways → 410 Gone. (It answered 500 before; it never returned data.) |
Use GET /api/clusters/{id}/cluster-destinations and /skill-adj-clusters. The O*NET form /api/occupations/{onet}/pathways is unchanged. |
| B4 | 0.16.1 | TITLECONVERT returns distinct clusters on the no-company path (was distinct O*NET codes). | limit now counts clusters. If you relied on seeing sibling O*NET codes, use group=onet. |
| B5 | 0.16.2 | match_source can be compound, e.g. name_corpus+catalog_exact. |
Test with membership ("catalog" in s), not equality. |
| B6 | 0.16.0 | MCP: POST /mcp/v1 removed (404). |
Use POST /mcp. Same tools/call body. Always send a Bearer token; tool errors now come back as HTTP 200 with the error in the JSON-RPC body (was 400). |
| B7 | 0.16.10 | MCP: single-wrapped result. content[0].text parses to the DSL envelope in one json.loads (was two); also available as result.structuredContent. The format argument is retired. |
Parse once, or unwrap while the object still has a content key (works with both shapes). Sending format is harmless — it is ignored. |
| B8 | 0.23.0 | premium_skill_description_1/2/3 are always null. The provider replaced per-skill descriptions with one paragraph. |
Show premium_skills_summary instead. The keys stay in the response. |
| B9 | 0.23.0 | Premium skills blank for ~300 more clusters. The 10.23 delivery publishes skills for 479 of 794 clusters (was ~765). | Handle premium_skill_N: null and premium_skills_summary: null. |
2. New response fields (additive)¶
2.1 AI impact scores — ai_impact_scores (0.23.0, SUBJECT TO CHANGE)¶
Three continuous scores per cluster from BGI's 2026-10-23 research:
demand_score (−5…+5), entry_level_score (−5…+5), skills_change_score (0…5).
Where: GET /api/clusters/{id}/ai-impact and /api/occupations/cluster:{id}/ai-impact
(top-level key) · GET /api/occupations/{onet}/ai-impact (inside data, plus
occupation_scores) · GET /api/clusters/{id} (inside data).
Coverage: 169 of 794 clusters. For the rest:
"ai_impact_scores": {"available": false, "message": "AI impact information not available at this time."}
When scores exist (GET /api/clusters/94/ai-impact, abridged):
"ai_impact_scores": {
"available": true,
"demand_score": -3.8,
"entry_level_score": -0.2,
"skills_change_score": 1.8,
"method": "Weighted avg by monthly postings",
"methodology_version": "2026-10-23",
"occupations_with_data": [
{"onet_code": "19-1029.02", "title": "Molecular and Cellular Biologists",
"monthly_postings": 1397.5, "demand_score": -5.0, "entry_level_score": 0.5, "skills_change_score": 1.1}
],
"occupations_without_data": [
{"onet_code": "19-1029.00", "title": "Biological Scientists, All Other"},
{"onet_code": "19-1029.04", "title": "Biologists"}
],
"notes": ["These scores were computed from 3 of 5 occupations in this cluster and do not describe those in occupations_without_data. ..."],
"scales": {"demand_score": "-5 to +5. ...", "entry_level_score": "-5 to +5. ...", "skills_change_score": "0 to 5. ..."},
"note": "Scores are continuous and published to one decimal. Report them as published; ..."
}
Rules for display:
available: false→ show themessage(or omit the section). Don't estimate from neighbouring clusters.- A
nullscore in an available block was not published (14 clusters have no entry-level score, 1 has no skills-change score). Don't fill it. - Block itself
null→ our read failed. Treat as an error and retry, not as "no data". - Quote scores to one decimal. BGI publishes no bands or labels; don't add "high/low".
- If
occupations_without_datais non-empty, the score doesn't describe those occupations — readnotes. - Occupation form:
occupation_scoresis that occupation's own row, ornull(with a note) when the cluster score wasn't computed from it.
2.2 Cluster text (0.23.0)¶
| Field | Where | Notes |
|---|---|---|
cluster_description |
GET /api/clusters/{id} |
What the work is, one or two sentences. All 794 clusters |
premium_skills_summary |
GET /api/clusters/{id}, both AI-impact endpoints, /api/occupations/{onet}/pathways |
One paragraph covering all the cluster's premium skills; null when it has none |
2.3 Wage growth — wage_growth (0.17.5)¶
On /api/occupations/{onet}/wages (beside wages_by_segment), /api/clusters/{id}/wages
(beside data) and GET /api/clusters/{id} (inside data). null when unpublished.
p50_growth_0_10is employment-weighted across metro sizes; the wage-row percentiles are not. Computing growth from the rows gives a different number — that's expected. Quote one and say which.tieris relative to one delivery, not an absolute band.
2.4 TITLECONVERT display label (0.17.7)¶
Every /api/titleconvert row gains display_name and display_source
(cluster_name | onet_title | bgi_suboccupation) — the name that most directly
matches what was typed. Render display_name || cluster_name || onet_title.
| Typed | cluster_name |
display_name |
|---|---|---|
creative director |
Art Directors | Creative Director |
civil |
Infrastructure Engineers | Civil Engineers |
2.5 Adjacency data_status (0.16.8)¶
Every row and the baseline on /skill-adj-clusters and /cluster-destinations carry
data_status: attributed or taxonomy_only. A taxonomy_only baseline (e.g. Chief
Executives, military occupations) now returns 200 with data: [] and a
data_status_note instead of an "Unknown cluster_id" error.
2.6 Other¶
?years_in=7filter on the wage endpoints (0.17.0).premium_skill_details(skill + description) on/pathwaysand/ai-impact(0.17.0; descriptionsnullsince 0.23.0).query_info.hint_appliedon/api/titleconvertwhen a curated mapping answered the title (0.15.18);match_sourceis thenhint.
3. Behaviour changes with no shape change¶
| Since | Change |
|---|---|
| 0.16.1 | Boolean query params (_fast, clarify, _catalog, ...) accept 1/true/yes/on; previously only true worked and ?x=1 read as false. |
| 0.16.2 | TITLECONVERT confidence reports high/low from source agreement on the catalog path. Use it, not match_score, to decide whether to confirm with the user. |
| 0.16.2 – 0.18.6 | TITLECONVERT accuracy and speed work: larger name catalog, provider suboccupation names as first-class aliases, typeahead ranked by usage. Same request/response shape; top results may differ from what you saw before. |
| 0.16.11 / 0.17.0 | Adjacency neighbours are a curated, complete set — not a sample or top-N. Destinations widen from 3 to 5. Iterate premium_skill_N / skill_adj_cluster_N keys rather than hardcoding three; read count/total. |
| 0.17.0 | ai_flag deprecated — still served and filterable; no longer updated. Prefer ai_impact_scores. |
| 0.17.0 | /api/clusters/{id}/retention and /api/companies/{name}/hiring now have a stable sort order. |
| 0.22.3 | COMPANYCONVERT matches one-word typos it used to miss (deloite → Deloitte). |
4. Dataset counts (0.23.0-beta)¶
| 0.15.16 doc | Now | |
|---|---|---|
| Companies | ~1,750 | ~1,750 |
| O*NET occupations | 1,016 | 948 |
| Clusters with attributes | 561* | 794 |
| Company × occupation-cluster pairs | ~55,000 | ~38,500 |
| Clusters with AI impact scores | — | 169 |
Clusters with an ai_flag |
177 | 176 |
* The 0.15.16 doc's figure; it predates the 9.22 and 10.23 refreshes.
5. Client checklist¶
- [ ] Wage code reads
years_in, expects 11 rows per MSA size (B1) - [ ] Not-found on an old
cluster_id/ O*NET code is handled (B2) - [ ] No calls to
/api/occupations/cluster:{id}/pathways(B3) - [ ]
match_sourcechecked by membership (B5) - [ ] MCP clients on
POST /mcp, parsing the result once (B6, B7) - [ ] UI shows
premium_skills_summary, not per-skill descriptions (B8) - [ ] Nulls handled for premium skills and the summary (B9)
- [ ] TITLECONVERT UI renders
display_name(2.4) - [ ] AI impact section handles
available: false,nullscores, and anullblock differently (2.1)
Questions: tech@schultzfamilyfoundation.org