Sharing some feedback based on my first pass of testing v2.
A. Query parameters documented in the v2 OpenAPI spec that do not behave as documented
A1. Fide=false behaves identically to Fide=true
Fide=true correctly returns only members with a FIDE ID. Fide=false returns
the same filtered set instead of the complement.
for f in "" "&Fide=true" "&Fide=false"; do
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/members?Fuzzy=smith&Size=50$f" \\
| jq "\[.items\[\] | select(.fideId != null)\] | length"
done
# 12 <- no filter (baseline)
# 50 <- Fide=true (correct)
# 50 <- Fide=false (expected 0)
A2. UseUnofficialRatings has no effect
true and false return identical ratings, and neither matches the actual
unofficial post-event rating.
for u in true false; do
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/members?Fuzzy=12743305&UseUnofficialRatings=$u" \\
| jq -r "\\"$u -> \\(.items\[0\].ratings\[\] | select(.ratingSystem==\\"R\\") | .rating)\\""
done
# true -> 2870
# false -> 2870
curl -s "https://ratings-api.uschess.org/api/v1/members/12743305/sections?Size=1" \\
| jq '.items\[0\].ratingRecords\[0\] | {preRating, postRating}'
# { "preRating": 2863, "postRating": 2852 } <- event ended 2026-08-19
2870 is the official supplement rating in both cases. The unofficial rating
(2852) is not reachable through v2 under either setting.
A3. Size on /api/v2/members is silently capped at 100
The parameter is documented, but carries no maximum. Over-large values are
clamped rather than rejected, and nothing but the echoed pageSize indicates the
requested size was not honored.
for s in 100 101 500; do
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/members?Fuzzy=smith&Size=$s" \\
| jq -r "\\"Size=$s -> items=\\(.items|length) pageSize=\\(.pageSize)\\""
done
# Size=100 -> items=100 pageSize=100
# Size=101 -> items=100 pageSize=100
# Size=500 -> items=100 pageSize=100
Adding maximum: 100 to the parameter schema would be enough.
B. Response fields declared in the spec that carry no data
Both fields are nullable: true in the spec, so always returning null is not
strictly a contract violation. We raise them because the field exists with no
reachable value, and in B2’s case the data was previously served.
B1. uscfTitle is never populated
MemberDetailDto.uscfTitle returns null for every member we checked,
including all 100 top-ranked US members. v1’s `/awards` shows these same players
holding USCF ratings titles.
curl -s -H "X-Api-Key: $KEY" \\
https://ratings-api.uschess.org/api/v2/members/12743305 | jq '.uscfTitle'
# null <- Fabiano Caruana, US #1
curl -s https://ratings-api.uschess.org/api/v1/members/12743305/awards \\
| jq '\[.items\[\] | select(.category=="RatingsTitle") | .type\] | unique'
# \["NationalMaster","OriginalLifeMaster"\]
Same result for Nakamura (12641216), So (13145890), Aronian (15218444).
There is no other title field in v2, so NM/LM currently has no v2 source.
B2. gamesPlayed is missing for established ratings — a regression
MemberRatingDto.gamesPlayed is returned only when isProvisional: true. Across
600 rating objects from the top 100 members, \0 of 384* established ratings
carried a game count.
curl -s -H "X-Api-Key: $KEY" \\
https://ratings-api.uschess.org/api/v2/members/12641216 \\
| jq '.ratings\[\] | {ratingSystem, rating, isProvisional, gamesPlayed}'
# {"ratingSystem":"R","rating":2849,"isProvisional":false,"gamesPlayed":null}
# {"ratingSystem":"Q","rating":2724,"isProvisional":false,"gamesPlayed":null}
# {"ratingSystem":"B","rating":2911,"isProvisional":false,"gamesPlayed":null}
# {"ratingSystem":"OR","rating":2747,"isProvisional":true,"gamesPlayed":21}
We observed this data previously. Our captured response for this same member
(Nakamura) returned gamesPlayed: 1566 for OverTheBoardRegular and 180 for
OverTheBoardBlitz; the provisional OnlineRegular value of 21 is unchanged.
v1 /members/{id} now behaves the same way, so the change affects both versions.
Was this change intentional?
B3. ordinal is never populated
MemberDetailDto.ordinal is declared {"type": "integer", "format": "int32", "nullable": true} and never appears in a response, on either the list or the
detail endpoint, for any of the same 100 members. rank and stateRank are
populated on those records, so it is not obvious what `ordinal` would add.
ordinal and uscfTitle are the only two declared MemberDetailDto properties
that never appear; every other property in the schema was observed with a value
on at least one member.
C. Other Issues
C1. gms* columns in AllRatings.tsv are always empty
Uscf-YYYY-MM-AllRatings.tsv declares six game-count columns. All six are empty
in every one of the 1,283,760 rows of Uscf-2026-01-AllRatings.tsv, while
the adjacent rtg* columns are populated.
curl -s -H "X-Api-Key: $KEY" \\
https://ratings-api.uschess.org/api/v2/rating-supplement-files/Uscf-2026-01-AllRatings.tsv \\
| awk -F'\\t' 'NR>1{for(i=10;i<=21;i++) if($i!="") c\[i\]++}
END{print "rtgreg",c\[10\]; print "gmsreg",c\[11\];
print "rtgquick",c\[12\]; print "gmsquick",c\[13\]}'
# rtgreg 865378
# gmsreg 0
# rtgquick 564791
# gmsquick 0
Game counts do appear, but only encoded inside the rating value as 2194/18 for
provisional players; established players show 1789*. Combined with B2, there is
currently no way to obtain an established player’s game count from v2.
C2. AllMembers-Blitz.dbf stores blitz ratings under Q_* field names
The Blitz and Quick AllMembers DBFs declare an identical field list —
R_PLR_TYP, R_LPB_RAT, R_NRM_DAT, Q_PLR_TYP, Q_LPB_RAT, Q_NRM_DAT.
There is no B_* field. In the Blitz file the Q_* columns actually contain
blitz data:
| Member |
Quick file `Q_LPB_RAT` |
Blitz file `Q_LPB_RAT` |
live Q |
live B |
| Caruana (12743305) |
2753 |
2852 |
2736 |
2799 |
| Nakamura (12641216) |
2765 |
2959 |
2724 |
2911 |
| So (13145890) |
2695 |
2858 |
2683 |
2879 |
(DBF values are the Dec 2025 supplement, hence higher than today’s.) A consumer
parsing by field name reads blitz ratings as quick ones with no error. Renaming
the columns to B_* in the Blitz file would fix it, unless this naming is intentional for some reason.
C3. /rating-supplement-files uses different pagination parameter names
The spec documents offset/pageSize here and Offset/Size on every other
paged operation, so the server matches its contract — but the inconsistency is
easy to trip over, and the wrong spelling is silently ignored rather than
rejected.
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/rating-supplement-files?Size=5" \\
| jq '.items | length'
# 50 <- Size ignored, default applied
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/rating-supplement-files?pageSize=5" \\
| jq '.items | length'
# 5
C4. Minor: UsePeak changes ordering but no peak value is exposed
UsePeak=true does affect sorting, but the returned DTO contains current ratings
only, so the value being sorted on cannot be read back.
for u in true false; do
curl -s -H "X-Api-Key: $KEY" \\
"https://ratings-api.uschess.org/api/v2/members?SortBy=Rating&Dir=Desc&Ranked=true&UsePeak=$u&Size=5" \\
| jq -c "\[.items\[\] | {n:.lastName, r:(.ratings\[\]|select(.ratingSystem==\\"R\\")|.rating)}\]"
done
# true : Caruana 2870, SO 2837, NAKAMURA 2849, Aronian 2795, DOMINGUEZ 2782
# false: Caruana 2870, NAKAMURA 2849, SO 2837, LE 2799, Aronian 2795
With UsePeak=true the list is no longer ordered by any returned field.