Someone links an Internet-Draft, calls it a new standard, and now you have to decide whether to build against it. The draft looks official. It has a document number, section headings full of MUST and SHOULD, an IANA Considerations section and a Normative References list. None of that tells you what stage it is at.
The IETF publishes the answer as structured data. You can get it in one HTTP request, and the fields you need are all null or not null, so the check fits in a few lines of code.
The filename already tells you something
Every Internet-Draft name follows a pattern:
draft-<who-submitted-it>-<topic>-<revision>
If the second segment is ietf, a working group adopted the document and owns it. draft-ietf-dmarc-dmarcbis is the DMARC working group's revision of DMARC. Anything else is the submitter's own name or their employer's. draft-brotman-aggregate-performance-reporting is an individual submission by Alex Brotman.
That is a signal and it costs nothing to read. It is not the whole answer, because a document can be adopted later and keep circulating under the old name in people's bookmarks, so the rename matters and you want the current record.
One request to the datatracker
The IETF datatracker has a read-only REST API at /api/v1/. No key, no signup.
curl -s "https://datatracker.ietf.org/api/v1/doc/document/?name=draft-brotman-aggregate-performance-reporting&format=json" \
| jq '.objects[0] | {name, rev, stream, intended_std_level, ad, shepherd, rfc_number, pages, expires}'
Run on 13 September 2026, that returns:
{
"name": "draft-brotman-aggregate-performance-reporting",
"rev": "01",
"stream": null,
"intended_std_level": null,
"ad": null,
"shepherd": null,
"rfc_number": null,
"pages": 14,
"expires": "2027-03-12T15:50:05Z"
}
Five nulls in a row. Now the same query against the DMARC working group document:
{
"name": "draft-ietf-dmarc-dmarcbis",
"rev": "41",
"stream": "/api/v1/name/streamname/ietf/",
"intended_std_level": "/api/v1/name/intendedstdlevelname/ps/",
"ad": "/api/v1/person/person/106842/",
"shepherd": "/api/v1/person/email/tjw.ietf@gmail.com/",
"rfc_number": null,
"pages": 83,
"expires": "2025-10-06T21:29:30Z"
}
Same API, same shape, completely different picture. Revision 41 instead of 01. A stream. An intended status. A named Area Director and a named shepherd.
What each field is actually telling you
stream is the publication path a document is on. GET /api/v1/name/streamname/ lists all six: IETF, ISE (Independent Submission), IRTF, IAB, Editorial, Legacy. null means the document is not on any of them yet. This is the single most useful field, because a document with no stream has no group of people responsible for moving it forward.
intended_std_level is what the document is aiming to become. Dereference the URI to get the human name:
curl -s "https://datatracker.ietf.org/api/v1/name/intendedstdlevelname/ps/?format=json" | jq -r .name
# Proposed Standard
Worth knowing: the text inside a draft can say Intended status: Standards Track in its own header while this field is null. The header line is typed by the author into the document template. The API field is set through the process. When they disagree, they are answering different questions, and the API field is the one that reflects where the document sits.
ad and shepherd are the two humans assigned to shove a document through publication. Both null means nobody has been assigned, which means the publication process has not started.
rfc_number is null until the document is published as an RFC. Once it is set, the draft is history and you should be reading the RFC.
group is the working group. Individual submissions all point at the same placeholder group, which is worth resolving once so you recognise it:
curl -s "https://datatracker.ietf.org/api/v1/group/group/1027/?format=json" | jq '{acronym, name, type}'
{
"acronym": "none",
"name": "Individual Submissions",
"type": "/api/v1/name/grouptypename/individ/"
}
states is a list of state URIs. The one from the draft-iesg state type is the one people mean by "where is it":
curl -s "https://datatracker.ietf.org/api/v1/doc/state/150/?format=json" | jq '{name, desc}'
{
"name": "I-D Exists",
"desc": "The IESG has not started processing this draft, or has stopped processing it without publication."
}
That description is the clearest sentence on the whole site. It is the IESG saying, in its own data model, that nothing has happened.
The whole check in one function
This is small enough to keep in a scratch file and run whenever a draft turns up in a thread.
import json
import urllib.request
API = "https://datatracker.ietf.org/api/v1"
def draft_status(name):
url = f"{API}/doc/document/?name={name}&format=json"
with urllib.request.urlopen(url) as r:
objects = json.load(r)["objects"]
if not objects:
return f"{name}: no such document"
d = objects[0]
adopted = name.split("-")[1] == "ietf"
signals = {
"adopted by a working group": adopted,
"on a publication stream": d["stream"] is not None,
"has an intended status": d["intended_std_level"] is not None,
"has a responsible AD": d["ad"] is not None,
"has a shepherd": d["shepherd"] is not None,
"published as an RFC": d["rfc_number"] is not None,
}
print(f"{name}-{d['rev']} ({d['pages']} pages, expires {d['expires'][:10]})")
for label, value in signals.items():
print(f" {'yes' if value else 'no ':<3} {label}")
print(f" {sum(signals.values())}/6 signals present")
draft_status("draft-brotman-aggregate-performance-reporting")
draft_status("draft-ietf-dmarc-dmarcbis")
Output on 13 September 2026:
draft-brotman-aggregate-performance-reporting-01 (14 pages, expires 2027-03-12)
no adopted by a working group
no on a publication stream
no has an intended status
no has a responsible AD
no has a shepherd
no published as an RFC
0/6 signals present
draft-ietf-dmarc-dmarcbis-41 (83 pages, expires 2025-10-06)
yes adopted by a working group
yes on a publication stream
yes has an intended status
yes has a responsible AD
yes has a shepherd
no published as an RFC
5/6 signals present
Note the expiry dates. The individual submission expires in 2027 and the working group document expired in 2025. An expiry date in the past is not a signal of abandonment. Drafts lapse after six months and get replaced by an RFC or a newer revision, and the record stays. Use rev and the time field for freshness, not expires.
Watching a draft instead of checking it once
If you have decided a draft is worth tracking, the field to store is time, which is when the record last changed. Poll it and compare.
curl -s "https://datatracker.ietf.org/api/v1/doc/document/?name=$1&format=json" \
| jq -r '.objects[0] | "\(.rev) \(.time)"'
For the Brotman draft that prints 01 2026-09-08T15:50:05Z, so the revision that is live today went up on 8 September 2026. Store that string, run the same command on a schedule, and diff. When rev moves you have a new version to read. When stream stops being null, the document has been adopted and the questions above change.
The datatracker also exposes a /api/v1/submit/submission/ endpoint, and the document record carries a list of submission URIs, one per revision. That list is the cheapest way to see how many times a document has actually been revised without scraping the history tab.
What none of this measures
Zero out of six is not a criticism of a document. Individual submissions are the normal front door, and DMARC itself came in through it. Run the same query against draft-kucherawy-dmarc-base and you get group 1027, the same Individual Submissions placeholder, stream set to ise rather than ietf, and rev stopping at 13 with an expiry of 2015-08-10. That document became RFC 7489, on the Independent Submission stream, without a working group ever adopting it. The DMARC working group and draft-ietf-dmarc-dmarcbis came afterwards.
What the six signals tell you is narrower and more useful: how much process has run, and therefore how much the wire format is likely to move under you. A draft at 0/6 can change shape completely at the next revision, because nobody has agreed to anything yet. A document at 5/6 with an AD attached has had its incompatible changes argued out already.
There is also a plain-language version of all of this on every datatracker page, in a yellow box, above the metadata. For an unadopted draft it reads: "This I-D is not endorsed by the IETF and has no formal standing in the IETF standards process." The API just lets you check a list of them without opening fifteen tabs.
Most of what I build sits on email authentication records, which is an area with a lot of draft traffic, so this check runs before I read a new proposal rather than after. It takes about ten seconds. Implementing the wrong revision of a wire format takes a weekend.
Written with AI assistance from my own notes and test results. I checked every fact and command before publishing. The cover image was generated with DEV's built-in image tool.
Top comments (0)