Spec: JSON Feed v1 - simplepie/simplepie-ng GitHub Wiki
Updated: v1.0.0 2017-05-17
by Brent Simmons and Manton Reece
Table of Contents
The JSON Feed format is a pragmatic syndication format, like RSS and Atom, but with one big difference: it’s JSON instead of XML.
For most developers, JSON is far easier to read and write than XML. Developers may groan at picking up an XML parser, but decoding JSON is often just a single line of code.
Our hope is that, because of the lightness of JSON and simplicity of the JSON Feed format, developers will be more attracted to developing for the open web.
Here’s a simple example:
{
"version": "https://jsonfeed.org/version/1",
"title": "My Example Feed",
"home_page_url": "https://example.org/",
"feed_url": "https://example.org/feed.json",
"items": [
{
"id": "2",
"content_text": "This is a second item.",
"url": "https://example.org/second-item"
},
{
"id": "1",
"content_html": "<p>Hello, world!</p>",
"url": "https://example.org/initial-post"
}
]
}
It supports more than just the above, of course.
After making feeds easier to read and write, our second goal is to provide a format that’s self- documenting and difficult to do wrong.
We also want to support:
- Microblogs, which are often plain text and without titles. So much web writing today is Twitter- like, which is actually plain text.
- Multiple attachments.
- Modern needs such as avatar images, feed icons and favicons, and banner and featured images. Feed readers should not have to search and scrape to guess at these things.
- A simple way to add extensions.
(Note about plain text: on this page it means “not HTML” — emojis, for instance, are considered (plain text.)
A JSON Feed is a list that may change over time, and the individual items in the list may change.
Think of a blog or microblog, Twitter or Facebook timeline, set of commits to a repository, or even a server log. These are all lists, and each could be described by a feed.
A JSON Feed starts with some info at the top: it says where the feed comes from, and may say who created it and so on.
After that there’s an array of objects — items
— that describe each object in the list.
-
version
(required, string) is the URL of the version of the format the feed uses. This should appear at the very top, though we recognize that not all JSON generators allow for ordering. -
title
(required, string) is the name of the feed, which will often correspond to the name of the website (blog, for instance), though not necessarily. -
home_page_url
(optional but strongly recommended, string) is the URL of the resource that the feed describes. This resource may or may not actually be a “home” page, but it should be an HTML page. If a feed is published on the public web, this should be considered as required. But it may not make sense in the case of a file created on a desktop computer, when that file is not shared or is shared only privately. -
feed_url
(optional but strongly recommended, string) is the URL of the feed, and serves as the unique identifier for the feed. As withhome_page_url
, this should be considered required for feeds on the public web. -
description
(optional, string) provides more detail, beyond thetitle
, on what the feed is about. A feed reader may display this text. -
user_comment
(optional, string) is a description of the purpose of the feed. This is for the use of people looking at the raw JSON, and should be ignored by feed readers. -
next_url
(optional, string) is the URL of a feed that provides the next n items, where n is determined by the publisher. This allows for pagination, but with the expectation that reader software is not required to use it and probably won’t use it very often.next_url
must not be the same asfeed_url
, and it must not be the same as a previousnext_url
(to avoid infinite loops). -
icon
(optional, string) is the URL of an image for the feed suitable to be used in a timeline, much the way an avatar might be used. It should be square and relatively large — such as 512 x 512 — so that it can be scaled-down and so that it can look good on retina displays. It should use transparency where appropriate, since it may be rendered on a non-white background. -
favicon
(optional, string) is the URL of an image for the feed suitable to be used in a source list. It should be square and relatively small, but not smaller than 64 x 64 (so that it can look good on retina displays). As withicon
, this image should use transparency where appropriate, since it may be rendered on a non-white background. -
author
(optional, object) specifies the feed author. The author object has several members. These are all optional — but if you provide an author object, then at least one is required:-
name
(optional, string) is the author’s name. -
url
(optional, string) is the URL of a site owned by the author. It could be a blog, micro- blog, Twitter account, and so on. Ideally the linked-to page provides a way to contact the author, but that’s not required. The URL could be a mailto: link, though we suspect that will be rare. -
avatar
(optional, string) is the URL for an image for the author. As withicon
, it should be square and relatively large — such as 512 x 512 — and should use transparency where appropriate, since it may be rendered on a non-white background.
-
-
expired
(optional, boolean) says whether or not the feed is finished — that is, whether or not it will ever update again. A feed for a temporary event, such as an instance of the Olympics, could expire. If the value istrue
, then it’s expired. Any other value, or the absence ofexpired
, means the feed may continue to update. -
hubs
(very optional, array of objects) describes endpoints that can be used to subscribe to real-time notifications from the publisher of this feed. Each object has atype
andurl
, both of which are required. See the section “Subscribing to Real-time Notifications” below for details.
items
is an array, and is required. An item includes:
-
id
(required, string) is unique for that item for that feed over time. If an item is ever updated, theid
should be unchanged. New items should never use a previously-usedid
. If anid
is presented as a number or other type, a JSON Feed reader must coerce it to a string. Ideally, theid
is the full URL of the resource described by the item, since URLs make great unique identifiers. -
url
(optional, string) is the URL of the resource described by the item. It’s the permalink. This may be the same as theid
— but should be present regardless. -
external_url
(very optional, string) is the URL of a page elsewhere. This is especially useful for linkblogs. Ifurl
links to where you’re talking about a thing, thenexternal_url
links to the thing you’re talking about. -
title
(optional, string) is plain text. Microblog items in particular may omit titles. -
content_html
andcontent_text
are each optional strings — but one or both must be present. This is the HTML or plain text of the item. Important: the only place HTML is allowed in this format is incontent_html
. A Twitter-like service might usecontent_text
, while a blog might usecontent_html
. Use whichever makes sense for your resource. (It doesn’t even have to be the same for each item in a feed.) -
summary
(optional, string) is a plain text sentence or two describing the item. This might be presented in a timeline, for instance, where a detail view would display all ofcontent_html
orcontent_text
. -
image
(optional, string) is the URL of the main image for the item. This image may also appear in thecontent_html
— if so, it’s a hint to the feed reader that this is the main, featured image. Feed readers may use the image as a preview (probably resized as a thumbnail and placed in a timeline). -
banner_image
(optional, string) is the URL of an image to use as a banner. Some blogging systems (such as Medium) display a different banner image chosen to go with each post, but that image wouldn’t otherwise appear in thecontent_html
. A feed reader with a detail view may choose to show this banner image at the top of the detail view, possibly with the title overlaid. -
date_published
(optional, string) specifies the date in RFC 3339 format. (Example:2010-02-07T14:04:00-05:00
.) -
date_modified
(optional, string) specifies the modification date in RFC 3339 format. -
author
(optional, object) has the same structure as the top-levelauthor
. If not specified in an item, then the top-levelauthor
, if present, is the author of the item. -
tags
(optional, array of strings) can have any plain text values you want. Tags tend to be just one word, but they may be anything. Note: they are not the equivalent of Twitter hashtags. Some blogging systems and other feed formats call these categories.
An individual item may have one or more attachments.
-
attachments
(optional, array) lists related resources. Podcasts, for instance, would include an attachment that’s an audio or video file. Each attachment has several members:-
url
(required, string) specifies the location of the attachment. -
mime_type
(required, string) specifies the type of the attachment, such as “audio/mpeg.” -
title
(optional, string) is a name for the attachment. Important: if there are multiple attachments, and two or more have the exact sametitle
(whentitle
is present), then they are considered as alternate representations of the same thing. In this way a podcaster, for instance, might provide an audio recording in different formats. -
size_in_bytes
(optional, number) specifies how large the file is. -
duration_in_seconds
(optional, number) specifies how long it takes to listen to or watch, when played at normal speed.
-
Publishers can use custom objects in JSON Feeds. Names must start with an _
character followed by
a letter. Custom objects can appear anywhere in a feed.
For instance, imagine a podcast directory service — call it Blue Shed Podcasts — that asks a podcaster to specify some additional information about a feed or item. A publisher would do something like this:
"_blue_shed": {
"about": "https://blueshed-podcasts.com/json-feed-extension-docs",
"explicit": false,
"copyright": "1948 by George Orwell",
"owner": "Big Brother and the Holding Company",
"subtitle": "All shouting, all the time. Double. Plus. Good."
}
Feed readers that do not understand a given custom object must ignore it.
The about
string is there for a human looking at the feed, so they can understand what goes in the
custom extension. It’s optional, but it should appear at least once in a feed that may contain
multiple uses of _blue_shed
(preferably in the first use, but that’s not required).
Also, it’s good practice to name an extension with a company or service name, to provide a clue right away as to what it’s for and who made it.
Further naming rules: the extension name and its member keys must not contain any .
characters.
The extension name, and only the extension name, must begin with an _
character. (No standard keys
will ever begin with an _
— this is reserved for extensions.)
Member keys should be alphanumeric. That said, emojis are not illegal — we’re not heartless — and somebody’s going to use them.
Traditional feed readers usually poll a web site for changes at a regular interval. This is fine for
many applications, but there’s a more efficient approach for applications that need to know the
moment a feed changes. The top-level hubs
array points to one or more services that can be used by
feed aggregators to subscribe to changes to this feed. Those hubs can then send a notification to
the application as soon as the feed is updated.
The type
field describes the protocol used to talk with the hub, such as “rssCloud” or “WebSub.”
When using WebSub, the value for the JSON Feed’s feed_url
is passed for the hub.topic
parameter.
For more information about WebSub, see the specification at the
W3C.
A version 1 feed will be a valid version 2 feed, and so on. Future versions may add things, but won’t make older feeds invalid.
In future versions, defined keys will always adhere to these rules:
- Leading
_
characters are reserved for extensions. - Keys will be made of alphanumeric characters from the ASCII character set.
- Keys will never contain a
.
character.
JSON Feed files must be served using the same MIME type — application/json
— that’s used whenever
JSON is served.
The number of items in a feed is unlimited, but practical limits should be observed. A 1MB feed
places a burden on feed readers, especially if there are many such feeds. Under 100K is ideal, and
250K is fine. Use your judgment. If you need to provide older items, consider using pagination and
next_url
.
Publishers should support Conditional GET, to limit impact on their system when feed readers poll for changes.
Also, to further reduce bandwidth use, publishers should provide the top-level icon
and favicon
URLs. When not present, feed readers will often attempt to find these things by 1) downloading the
home page and scraping the HTML, and 2) making one or more requests to likely locations for these
images. To reduce traffic on your server, put this info in the feed.
JSON Feeds should be encoded using UTF-8 — but any encoding that’s legal JSON is legal for JSON Feeds.
Any publisher already publishing RSS and/or Atom should continue to do so. In fact, if you’re trying to decide which format (of RSS, Atom, and JSON Feed) to use, and you can do only one, pick RSS — it’s time-tested, popular, and good.
If a feed is invalid JSON, then we strongly recommend not attempting to parse it or use partial data. You can’t rely on it.
There are cases, though, where a required element might not be present. In the case of an
attachment
missing a mime_type
, you can probably deal with it fine. After all, when you download
the attached file, the web browser will provide a MIME type. (And you might have been able to guess
it from the file suffix.)
Another case might be a malformed date_published
that you can’t parse. You might substitute the
date the reader parsed it. (Feed readers have been doing that kind of thing for many years, for bad
or missing dates.)
As much as we’d like to encourage good feeds, we also emphasize that this is a pragmatic format, and the final test is user experience: if an error can be recovered from without significantly harming that experience, then it’s better than just refusing to use the feed (or part of the feed) at all.
That said, there is one thing we insist on: any item
without an id
must be discarded. We come to
this from years of experience dealing with feeds in other formats where unique identifiers are
optional. Without unique identifiers, it’s impossible to reliably refer to a given item
and its
changes over time, and this is terrible for user experience and becomes a source of bug reports to
you. (Your users will complain of “reruns.”) In that case, you may wish to alert the user that
there’s a problem with the feed, and you may also wish to contact the publisher.
For web-based feed readers, it would be helpful to publishers if you reported your subscription
numbers. To do this, add a substring of the form (n subscribers)
, where n is the actual number, to
the user-agent header in your http and https requests.
Feed readers are strongly advised to use Conditional GET, in order to minimize bandwidth and CPU cycles on clients and servers.
On the issue of which URL to use — url
or external_url
— when opening a web page: because an
item’s url
is always a permalink, the url
should be the default. For linkblogs that include an
external_url
, feed readers may give users a choice which URL to open: perhaps by displaying the
external_url
prominently in the UI, perhaps by a preference to use these URLs when present, or by
some other mechanism.
A web page should include a link
tag that specifies the location of the JSON Feed. Like this:
<link rel="alternate" title="My Feed" type="application/json" href="https://example.org/feed.json" />
This is the same as the feed discovery mechanism used for RSS and Atom.
The title
attribute is optional, but can be useful to differentiate multiple feeds — for instance,
there might be a feed for all blog posts and a feed for comments on a specific post. These should
have different titles.
In an ideal world, manual discovery — by a human using a web browser — is also possible, by adding
feed.json
to the resource the feed describes. If there’s a blog at https://example.org/
, then a
human could try https://example.org/feed.json
to find the feed.
However, we realize that there are reasons — both technical and matters of preference — that prevent this from being universal, which is fine. But still, if you can do this, it would be nice.
Publishers are encouraged to provide a link to their JSON Feed — as they do with RSS and Atom feeds — on their websites. It’s okay to make the link text “JSON Feed,” but a more specific title is also okay.
You can also use the JSON Feed icon as a link on your site, like this:
<div class="feedicon">
<a href="https://jsonfeed.org/feed.json">
<img src="https://jsonfeed.org/graphics/icon.png" />
</a>
</div>
(The authors thank Craig Hockenberry for the icon.)
This spec does not address the problem of how to go from clicking a link in a browser to getting a feed added to a feed reader — which, to be fair, may not always be the user intention. And of course a feed reader may be a local app or a web app.
However, the user_comment
is your chance to tell someone eyeing a bunch of raw JSON in their web
browser just what they’re looking at. Here’s an example:
"user_comment": "This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format. To add this feed to your reader, copy the following URL — https://example.org/feed.json — and add it your reader."
The below examples have just one item, for the sake of brevity in illustration. Normally a feed would have more than one item.
{
"version": "https://jsonfeed.org/version/1",
"user_comment": "This is a podcast feed. You can add this feed to your podcast client using the following URL: http://therecord.co/feed.json",
"title": "The Record",
"home_page_url": "http://therecord.co/",
"feed_url": "http://therecord.co/feed.json",
"items": [
{
"id": "http://therecord.co/chris-parrish",
"title": "Special #1 - Chris Parrish",
"url": "http://therecord.co/chris-parrish",
"content_text": "Chris has worked at Adobe and as a founder of Rogue Sheep, which won an Apple Design Award for Postage. Chris’s new company is Aged & Distilled with Guy English — which shipped Napkin, a Mac app for visual collaboration. Chris is also the co-host of The Record. He lives on Bainbridge Island, a quick ferry ride from Seattle.",
"content_html": "Chris has worked at <a href=\"http://adobe.com/\">Adobe</a> and as a founder of Rogue Sheep, which won an Apple Design Award for Postage. Chris’s new company is Aged & Distilled with Guy English — which shipped <a href=\"http://aged-and-distilled.com/napkin/\">Napkin</a>, a Mac app for visual collaboration. Chris is also the co-host of The Record. He lives on <a href=\"http://www.ci.bainbridge-isl.wa.us/\">Bainbridge Island</a>, a quick ferry ride from Seattle.",
"summary": "Brent interviews Chris Parrish, co-host of The Record and one-half of Aged & Distilled.",
"date_published": "2014-05-09T14:04:00-07:00",
"attachments": [
{
"url": "http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a",
"mime_type": "audio/x-m4a",
"size_in_bytes": 89970236,
"duration_in_seconds": 6629
}
]
}
]
}
Note that it uses both content_text
and content_html
, which is completely valid. An app such as
iTunes, for instance, might prefer to use content_text
, while a feed reader might prefer
content_html
.
Also note while there is just one attachment in this example, there could be several.
{
"version": "https://jsonfeed.org/version/1",
"user_comment": "This is a microblog feed. You can add this to your feed reader using the following URL: https://example.org/feed.json",
"title": "Brent Simmons’s Microblog",
"home_page_url": "https://example.org/",
"feed_url": "https://example.org/feed.json",
"author": {
"name": "Brent Simmons",
"url": "http://example.org/",
"avatar": "https://example.org/avatar.png"
},
"items": [
{
"id": "2347259",
"url": "https://example.org/2347259",
"content_text": "Cats are neat. \n\nhttps://example.org/cats",
"date_published": "2016-02-09T14:22:00-07:00"
}
]
}
Note that content_text
contains a URL. It’s still plain text. A reader may choose to turn URLs in
plain text into clickable links. Note also that there’s no title
.
There are some JSON Feeds on the web:
- Shape Of
- Flying Meat
- Maybe Pizza?
- Daring Fireball
- Hypercritical
- inessential
- Manton Reece
- Micro.blog timeline
- Timetable (podcast)
- The Record (podcast)
- Allen Pike
In addition, this website has its own JSON Feed.
The authors thank the following people for their extensive contributions and review, in alphabetical order: Allen Pike, Craig Hockenberry, Daniel Jalkut, Gus Mueller, Guy English, Jim Correia, Joe Heck, John Gruber, John Siracusa, Laura Savino, Marco Arment, and Paul Kafasis.