# API 2.0 is now available

**URL:** <https://forum.uschess.org/t/api-2-0-is-now-available/61581>\
**Category:** Running Chess Tournaments\
**Created:** [August 20, 2026, 5:47pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581 "2026-08-20T17:47:36Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 20, 2026, 5:47pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/1 "2026-08-20T17:47:36Z")

</div>

API 2.0 has been released for use by logged-in members and TDs.

To register an API key, use the TD Portal, which is open to TDs and logged-in members. The name field is the key name, which is not necessarily the member name. (Developers might choose to use a separate key for each app and use the app name as the key name.)

At this time, only the first tier has been implemented. This tier is intended primarily for 3rd party developers who need to be able to access ratings supplements and member data in their apps without going through the TD portal each time. Supplement and membership data is needed for tasks like validating tournament registrations or checking the membership status of players in a pairing app. Developers will have to have a valid US Chess membership and ID and a login to the US Chess website so their ID can be authenticated when a key is generated.

At this time, what we are thinking is that we will allow developers to register one or more API keys that they can use in their apps for all their users. That means the developers will be responsible for keeping their API Keys secure, such as by encrypting them or by not including the key in their code and fetching it from their server before each API call.

We will be tracking API usage by key and IP address. Excessive use may result in disabling an API key, or changes to the API protocols and rate limits

The alternative would probably be to provide API keys only to TDs and other members, which would require each app to have fields for entering that user’s API key in their profile settings.

Mike Nolan

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 20, 2026, 6:09pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/2 "2026-08-20T18:09:40Z")

</div>



---

<div class="post-metadata">

**Author:** ![TomDoan](https://avatars.discourse-cdn.com/v4/letter/t/8baadc/32.png) [@TomDoan](https://forum.uschess.org/u/TomDoan)\
**Post date:** [August 20, 2026, 10:20pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/3 "2026-08-20T22:20:39Z")

</div>

I’m trying to figure out the best way to handle this. What has been the biggest issue for WinTD users has been that (for years) you could get the allratings file downloaded and installed by clicking on a menu item, picking the supplement you wanted and the software would download it, unzip it and copy it to a local directory. That could be done whether or not you were a US Chess member. For the past eight months, they have had to go to the TD portal, download a text file, then install the downloaded file. Obviously considerably less automated, plus it required someone to login to the TD portal each month. If I were to have a common API key that is hard-coded into the software that I use only to download supplements (not to do single member accesses) couldn’t someone just hack out the API key by monitoring the activity out of WinTD and publish it. (I’m too old to do hacking, but I would imagine it’s not that hard). I don’t see that as being a good idea. My plan was to require everyone to get their own API key (which is saved only on the local machine) and if they want to share it with the world, that’s their problem, not mine. That requires someone within the organization to go to the TD Portal (just once) and get an API key and let everyone authorized know what it is and WinTD could take it from there.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 20, 2026, 10:51pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/4 "2026-08-20T22:51:44Z")

</div>

Actually, that was my preferred method of handling keys up front, but I guess Nick was worried about having several thousand keys to deal with. I’ll pass your note along to the developers and staff.

Developers would still need to register for an API key so that they can test that everything works.

---

<div class="post-metadata">

**Author:** ![russellb](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/russellb/32/3364_2.png) [@russellb](https://forum.uschess.org/u/russellb)\
**Post date:** [August 21, 2026, 12:19pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/5 "2026-08-21T12:19:08Z")

</div>

I can confirm that getting your key would be easy if an app is built this way with an API key included. I’m also not sure why anyone would care to do so if it only grants access to data that is already public, though. It would be a big problem if the API keys allowed access to write operations or access to private data of some kind.

This design is fine for hosted applications, but not for a client app talking directly to USCF if you intend for the keys to be secret.

---

<div class="post-metadata">

**Author:** ![ulmont](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/ulmont/32/151_2.png) [@ulmont](https://forum.uschess.org/u/ulmont)\
**Post date:** [August 21, 2026, 12:36pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/6 "2026-08-21T12:36:29Z")

</div>

> [@russellb](#):
>
> I’m also not sure why anyone would care to do so if it only grants access to data that is already public, though.

The rating supplement info is weirdly treated as “sorta confidential” when aggregated although each individual’s rating info is 100% public.

> [@nolan](#):
>
> API 2.0 has been released for use by logged-in members and TDs.

Mike, thank you; I’ll definitely update some workflows to use this. Do you have a list of the priority for any API 2.0 calls? I’m particularly thinking about, on the read side:

- Retrieve a full list of directed events (or just event ids) by TD (ideally with an optional time range restriction)
- Retrieve a full list of run events (or just event ids) by Affiliate (ideally with an optional time range restriction)
- Retrieve all data (full event info, full section info) for a list of event IDs

On the write side, of course, submitting an event is the holy grail.

---

<div class="post-metadata">

**Author:** ![russellb](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/russellb/32/3364_2.png) [@russellb](https://forum.uschess.org/u/russellb)\
**Post date:** [August 21, 2026, 2:26pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/7 "2026-08-21T14:26:50Z")

</div>

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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

---

<div class="post-metadata">

**Author:** ![TomDoan](https://avatars.discourse-cdn.com/v4/letter/t/8baadc/32.png) [@TomDoan](https://forum.uschess.org/u/TomDoan)\
**Post date:** [August 21, 2026, 2:44pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/8 "2026-08-21T14:44:47Z")

</div>

> [@russellb](#):
>
> I can confirm that getting your key would be easy if an app is built this way with an API key included. I’m also not sure why anyone would care to do so if it only grants access to data that is already public, though. It would be a big problem if the API keys allowed access to write operations or access to private data of some kind.
> 
> This design is fine for hosted applications, but not for a client app talking directly to USCF if you intend for the keys to be secret.

I assume that there is some concern that bots will overload the system. (It sounds like scraping MSA pages is already a problem). If API keys might get deactivated from what is seen as abuse, then you clearly would not want to have anything hard-coded that might be “leaked”.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 21, 2026, 3:51pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/9 "2026-08-21T15:51:19Z")

</div>

> [@TomDoan](#):
>
> assume that there is some concern that bots will overload the system. (It sounds like scraping MSA pages is already a problem). If API keys might get deactivated from what is seen as abuse, then you clearly would not want to have anything hard-coded that might be “leaked”.

Yes, the MSA system would basically lock up several times a week due to what I would attribute as scraping activities based on the logs, if I had to guess it was clubs updating their local records for their members. There’s also a nearly omnipresent level of spiders indexing the site. So when you add TDs and registration programs getting ready for upcoming events to all that, it was building up a huge backlog of SQL requests that would result in requests to that server ([www.uschess.org](http://www.uschess.org)) timing out.

I don’t know if we will have the same types of problems with MUIR and I don’t currently have access to the logs to check on that. We can, of course, increase the resources to the MUIR server, but that comes with a price tag.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 21, 2026, 3:59pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/10 "2026-08-21T15:59:57Z")

</div>

Thanks, Russell, I will pass these along to the developers.

The initial goal with API 2.0 was to restore the ability for pairing and registration programs to access published ratings (eg, supplements) and membership data.

Accessing current ratings is a bit more problematical, in part because unofficial ratings change frequently and any new events are likely to be out of sequence until the next rerate. (We are doing a 3 or 4 month rolling rerate every night and now a 4 year one once a week and ahead of the supplement.)

Also, official US Chess policy is that published ratings are preferred over unofficial ratings and there has been some reluctance to actively encourage and facilitate the use of unofficial ratings for events.

Accessing tournament data like crosstables via an API is likely to require upgrading to a higher tier key, and the details for the higher levels have not been fully discussed yet. There will probably need to be a signed data usage agreement and there may be an access fee to help cover the costs of providing higher bandwidth access.

At some point, the existing 1.0 APIs will either be cranked down to very low load limits or discontinued for public use.

Players, clubs and TDs should also be aware that we are working on plans to move the data on several servers, including [www.uschess.org](http://www.uschess.org), over to an archive server in the near future (possibly in September) at which point the MSA pages will have new URLs, and will ultimately go away once we’ve replaced all their functionality, probably in early 2027.

And here’s an early heads up on some planned system down time. We are looking to move the MUIR system to a different server account (with some cost savings) and this will likely require shutting down MUIR for most of a workday and assigning new IP addresses to affected servers. Also, the membership system development team wants to use that shutdown to do some upgrades, so large parts of the [uschess.org](http://uschess.org) website are likely to be down. This will probably be some time in September, either during the first week or the week of the 22nd.

---

<div class="post-metadata">

**Author:** ![ulmont](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/ulmont/32/151_2.png) [@ulmont](https://forum.uschess.org/u/ulmont)\
**Post date:** [August 21, 2026, 8:00pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/11 "2026-08-21T20:00:05Z")

</div>

@nolan The API 2.0 “Try it Out” buttons don’t work, somewhat funnily because they don’t include a dummy API key or a bypass.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 21, 2026, 9:00pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/12 "2026-08-21T21:00:19Z")

</div>

They work fine if you start by registering an API key and enter it then hit ‘authenticate’.

The authentication happens at the server end, there’s no way to bypass it on a demo page.

---

<div class="post-metadata">

**Author:** ![ulmont](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/ulmont/32/151_2.png) [@ulmont](https://forum.uschess.org/u/ulmont)\
**Post date:** [August 21, 2026, 9:05pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/13 "2026-08-21T21:05:05Z")

</div>

> [@nolan](#):
>
> They work fine if you start by registering an API key and enter it then hit ‘authenticate’.

I think “Authorize”, but that makes sense; I didn’t notice that button the first time around.

> [@nolan](#):
>
> The authentication happens at the server end, there’s no way to bypass it on a demo page.

This…seems wrong; the server could check if the origin was the demo page, for the most obvious option.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 21, 2026, 9:06pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/14 "2026-08-21T21:06:04Z")

</div>

> [@ulmont](#):
>
> This…seems wrong; the server could check if the origin was the demo page, for the most obvious option.

It’s not best practice to build bypass mechanisms in authentication procedures, and it might make it possible for non-members to use the member-only APIs.

---

<div class="post-metadata">

**Author:** ![russellb](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/russellb/32/3364_2.png) [@russellb](https://forum.uschess.org/u/russellb)\
**Post date:** [August 22, 2026, 12:36am UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/15 "2026-08-22T00:36:04Z")

</div>

That’s not really possible. The request comes from your browser. It’s easy to make a request that looks exactly like the one your browser makes from that page.

---

<div class="post-metadata">

**Author:** ![nolan](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/nolan/32/174_2.png) [@nolan](https://forum.uschess.org/u/nolan)\
**Post date:** [August 22, 2026, 12:52am UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/16 "2026-08-22T00:52:54Z")

</div>

As I recall, it even gives the curl command to generate that query on systems that support curl. (Linux, not sure what else.)

---

<div class="post-metadata">

**Author:** ![ulmont](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.uschess.org/ulmont/32/151_2.png) [@ulmont](https://forum.uschess.org/u/ulmont)\
**Post date:** [August 22, 2026, 2:47am UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/17 "2026-08-22T02:47:57Z")

</div>

I think everything supports curl by this point…but the curl exemplar I was looking at did not include the X-Api-Key header required. I did add that on my own command line for testing but the browser didn’t work.

> [@russellb](#):
>
> That’s not really possible. The request comes from your browser.

Wild. I had assumed my browser was triggering a server side request, but in retrospect yeah not sure how that could possibly work without a request starting from my browser.

---

<div class="post-metadata">

**Author:** ![TomDoan](https://avatars.discourse-cdn.com/v4/letter/t/8baadc/32.png) [@TomDoan](https://forum.uschess.org/u/TomDoan)\
**Post date:** [September 25, 2026, 7:45pm UTC](https://forum.uschess.org/t/api-2-0-is-now-available/61581/18 "2026-09-25T19:45:25Z")

</div>

> [@nolan](#):
>
> Actually, that was my preferred method of handling keys up front, but I guess Nick was worried about having several thousand keys to deal with. I’ll pass your note along to the developers and staff.
> 
> Developers would still need to register for an API key so that they can test that everything works.

I ended up putting a perl script on my web site that is used solely to download the allratings file. The API key is hidden on that and is used solely for doing the GDB downloads.
