├── .gitignore
├── lib
└── default.rb
├── .gitmodules
├── content
├── tag.md
├── stream.md
├── authentication.md
├── index.md
├── stylesheet.css
├── subscription.md
├── resources.md
├── rssconsensus.md
└── resources
│ └── feedsink-api.md
├── Rules
├── layouts
└── default.html
├── README.md
└── nanoc.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | tmp
2 | .DS_Store
3 | bin
4 | crash.log
5 | .bundle
6 | output
7 |
--------------------------------------------------------------------------------
/lib/default.rb:
--------------------------------------------------------------------------------
1 | # All files in the 'lib' directory will be loaded
2 | # before nanoc starts compiling.
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "gh-pages"]
2 | path = gh-pages
3 | url = git@github.com:rss-sync/Open-Reader-API.git
4 |
--------------------------------------------------------------------------------
/content/tag.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tag
3 | ---
4 |
5 | # Tag
6 |
7 | * TOC
8 | {:toc}
9 |
10 | TODO
11 | : content for this page
12 |
--------------------------------------------------------------------------------
/content/stream.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Stream
3 | ---
4 |
5 | # Stream
6 |
7 | * TOC
8 | {:toc}
9 |
10 | TODO
11 | : content for this page
12 |
--------------------------------------------------------------------------------
/content/authentication.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Authentication
3 | ---
4 |
5 | # Authentication
6 |
7 | * TOC
8 | {:toc}
9 |
10 | TODO
11 | : content for this page
12 |
--------------------------------------------------------------------------------
/Rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # A few helpful tips about the Rules file:
4 | #
5 | # * The string given to #compile and #route are matching patterns for
6 | # identifiers--not for paths. Therefore, you can’t match on extension.
7 | #
8 | # * The order of rules is important: for each item, only the first matching
9 | # rule is applied.
10 | #
11 | # * Item identifiers start and end with a slash (e.g. “/about/” for the file
12 | # “content/about.html”). To select all children, grandchildren, … of an
13 | # item, use the pattern “/about/*/”; “/about/*” will also select the parent,
14 | # because “*” matches zero or more characters.
15 |
16 | compile '/stylesheet/' do
17 | # don’t filter or layout
18 | end
19 |
20 | compile '*' do
21 | if item.binary?
22 | # don’t filter binary items
23 | else
24 | filter :erb
25 | filter :kramdown, :toc_levels => [2]
26 | layout 'default'
27 | end
28 | end
29 |
30 | route '/stylesheet/' do
31 | '/style.css'
32 | end
33 |
34 | route '*' do
35 | if item.binary?
36 | # Write item with identifier /foo/ to /foo.ext
37 | item.identifier.chop + '.' + item[:extension]
38 | else
39 | # Write item with identifier /foo/ to /foo/index.html
40 | item.identifier + 'index.html'
41 | end
42 | end
43 |
44 | layout '*', :erb
45 |
--------------------------------------------------------------------------------
/content/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Overview
3 | ---
4 |
5 | This is an effort to define a new **common** feed-syncing API protocol based (or not) on Google Reader. We recognise that we are many – many competing against each other – and many with conflicting agendas. But we come together here, putting aside our differences, to support RSS and the its ecosystem. Recognising that only by sharing a common platform can we make RSS easily consumable and help the average users … [Read the full statement](/Open-Reader-API/rssconsensus/).
6 |
7 | This aims to standardize all feed sync API initiatives.
8 |
9 | **Note: The documentation is preliminary.**
10 |
11 | ## Get involved
12 |
13 | 1. **[Voice your support](/Open-Reader-API/rssconsensus)**. This endeavor requires broad support. It will only work if all major feed syncing platforms decide to cooperate on a new protocol. First step is to agree, that we are all working our way towards the same common goal.
14 | 2. **[Join the mailing list](http://lists.ranchero.com/listinfo.cgi/rss-sync-ranchero.com)** and the discussion. It keeps you up-to-date. Plenty of good ideas have already been put forth and the discussions are well away.
15 | 3. **[Contribute](https://github.com/rss-sync/Open-Reader-API)**. How should the specifications look? *Well, that's for the developers of the protocol and **you** to decide.* All submissions are encouraged. To submit a change, fork [this repository](https://github.com/rss-sync/Open-Reader-API), commit your changes and send a [pull request](https://help.github.com/articles/using-pull-requests).
16 |
--------------------------------------------------------------------------------
/layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Open Reader API - <%= @item[:title] %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 | <%= yield %>
17 |
18 |
43 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Open Reader API
2 |
3 | This is the repo for the **[Open Reader API](http://rss-sync.github.io/Open-Reader-API/)** and is built with [nanoc](http://nanoc.ws/).
4 |
5 | All submissions are encouraged. To submit a change, [fork this repository](https://github.com/rss-sync/Open-Reader-API/fork), commit your changes and send a [pull request](https://help.github.com/articles/using-pull-requests).
6 |
7 | ## Making Contributions
8 |
9 | Making contributions or changes is easy. Clone this repo, alter the content within the content folder and submit a pull request.
10 |
11 | All content is created in plain text using the [Markdown](http://daringfireball.net/projects/markdown/) syntax. The folder structure will define the end url path structure so keep that in mind if new files and folders are added.
12 |
13 | The final display on http://rss-sync.github.io/Open-Reader-API is updated as needed manually using nanoc.
14 |
15 | ## Style Guide
16 |
17 | Markdown file extensions should be `.md`
18 |
19 | A single file should adhere to the following format:
20 |
21 | ---
22 | title: API Title
23 | ---
24 |
25 | # API Title
26 |
27 | * TOC
28 | {:toc}
29 |
30 | ## End point
31 |
32 | [VERB] /path
33 |
34 | ### Parameters
35 |
36 | Parameter 1:
37 | : - `option 1`: Option 1's definition
38 | - `option 2`: Option 2's definition
39 |
40 | Parameter 2
41 | : `option 1`, `option 2`, default: `option 3`
42 |
43 | ### Response
44 |
45 | Example response
46 |
47 |
48 | ## Testing Your Changes
49 |
50 | **Note:** Simply changing the Markdown files within `/content` is enough to make contributions or changes to this repo. The following is only for testing.
51 |
52 | If you want to test out your changes to ensure the final generation is correct, you can install the `nanoc` gem and `kramdown` gem which is used for Markdown formatting.
53 |
54 | view available nanoc commands with:
55 |
56 | nanoc -h
57 |
58 | Nanoc compiles the site into static files within the `output` folder. Run the compile with:
59 |
60 | nanoc compile
61 |
62 | You can use MAMP or a similar setup to view the resulting files or you can install the `adsf` gem which will work with nanoc. Then you can use:
63 |
64 | nanoc view
65 | open http://localhost:3000
66 |
67 | ## Updating the live site
68 |
69 | The [live site](http://rss-sync.github.io/Open-Reader-API/) lives in the [gh-pages branch](https://github.com/rss-sync/Open-Reader-API/tree/gh-pages). For convenience the gh-pages branch has been added to the main repo as a Git submodule.
70 |
71 | To update the live site, update master locally and run `nanoc compile`. The latest generated output will be in `/output/Open-Reader-API/`.
72 |
73 | Move into `/gh-pages` folder, clear it's content with `git rm -r *` and copy the output from the parent folder (`cp -r ../output/Open-Reader-API/ .`). Add all files and commit the changes.
74 |
75 | Pushing the gh-branch will update the live site. After a successful push, don't forget to update the master branch so that it's pointer to the submodule will have the proper commit hash stored.
76 |
--------------------------------------------------------------------------------
/content/stylesheet.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | font-family: Helvetica, Arial, sans-serif;
5 | }
6 |
7 | body {
8 | background: #fdfdfd;
9 | color: #111;
10 | font-size: 15px;
11 | line-height: 22px;
12 | }
13 |
14 | a:link,
15 | a:visited {
16 | color: #ff7c1b;
17 | text-decoration: none;
18 | }
19 |
20 | a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | h1,
25 | h2,
26 | h3,
27 | h4,
28 | h5,
29 | h6 {
30 | margin: 20px 0;
31 | }
32 |
33 | h2 {
34 | font-size: 1.5em;
35 | }
36 |
37 | #main h2 {
38 | margin-top: 60px;
39 | border-bottom: 1px solid #ccc;
40 | padding-bottom: 10px;
41 | }
42 |
43 | #header-wrapper{
44 | padding: 20px 40px;
45 | }
46 |
47 | #site-title {
48 | font-size: 40px;
49 | }
50 |
51 | #main {
52 | position: absolute;
53 | top: 80px;
54 | left: 40px;
55 | width: 500px;
56 | }
57 |
58 | #main h1 {
59 | font-size: 40px;
60 | font-weight: normal;
61 | line-height: 40px;
62 | letter-spacing: -1px;
63 | }
64 |
65 | #main p {
66 | margin: 20px 0;
67 | line-height: 20px;
68 | }
69 |
70 | #main ul, #main ol {
71 | margin: 20px;
72 | }
73 |
74 | #main li {
75 | list-style-position: outside;
76 | }
77 |
78 | #main ul li {
79 | list-style-type: disc;
80 | }
81 |
82 | #main li ol,
83 | #main li ul {
84 | margin-left: 20px !important;
85 | margin-top: 0;
86 | }
87 |
88 | #sidebar {
89 | position: absolute;
90 | top: 80px;
91 | left: 580px;
92 | width: 300px;
93 | }
94 |
95 | #sidebar #nav {
96 | padding: 10px 0 10px 20px;
97 | border-left: 1px solid #ccc;
98 | }
99 |
100 | #sidebar h2 {
101 | text-transform: uppercase;
102 | font-size: 13px;
103 | color: #333;
104 | letter-spacing: 1px;
105 | line-height: 20px;
106 | }
107 |
108 | #sidebar ul {
109 | list-style-type: none;
110 | margin: 20px 0;
111 | }
112 |
113 | #sidebar li {
114 | font-size: 14px;
115 | line-height: 20px;
116 | }
117 |
118 | pre,
119 | code,
120 | tt {
121 | font-size: 12px;
122 | font-family: Consolas, "Liberation Mono", Courier, monospace
123 | }
124 |
125 | code {
126 | margin: 0 2px;
127 | padding: 0 5px;
128 | background-color: #f8f8f8;
129 | border: 1px solid #cacaca;
130 | border-radius: 3px;
131 | white-space: nowrap;
132 | }
133 |
134 | pre {
135 | display: block;
136 | overflow: auto;
137 | background-color: #f8f8f8;
138 | border: 1px solid #cacaca;
139 | border-radius: 3px;
140 | line-height: 19px;
141 | padding: 6px 10px;
142 | margin: 15px 0;
143 | font-size: 13px;
144 | }
145 |
146 | pre code {
147 | margin: 0;
148 | padding: 0;
149 | border: none;
150 | background: transparent;
151 | white-space: pre;
152 | }
153 |
154 | dl {
155 | margin-left: 20px;
156 | }
157 |
158 | dt {
159 | font-size: 13px;
160 | margin-bottom:10px;
161 | font-weight: bold;
162 | }
163 |
164 | dd {
165 | margin-left: 10px;
166 | margin-bottom: 20px;
167 | }
168 |
169 | #main dd ul {
170 | margin: 10px 0 10px 5px;
171 | }
172 |
173 | #main #markdown-toc {
174 | }
175 |
176 | #sidebar #github {
177 | margin: 40px 0;
178 | font-size: 13px;
179 | border: 1px solid #cacaca;
180 | padding: 10px;
181 | }
182 |
183 | blockquote {
184 | border-left: 4px solid #DDD;
185 | padding: 0 15px;
186 | color: #777;
187 | }
188 |
--------------------------------------------------------------------------------
/content/subscription.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Subscription
3 | ---
4 |
5 | # Subscription
6 |
7 | * TOC
8 | {:toc}
9 |
10 | ## List Subscriptions
11 |
12 | GET /subscription/list
13 |
14 | ### Authentication
15 |
16 | Required
17 |
18 | ### Request Parameters
19 |
20 | ck
21 | : `timestamp`: Current timestamp to prevent caching issues.
22 |
23 | client
24 | : `application name`: String that identifies the client used.
25 |
26 | ### Response
27 |
28 | {
29 | "subscriptions": [
30 | {
31 | "id": "feed/http://feeds.feedburner.com/MyMicro-isv",
32 | "title": "47 Hats",
33 | "categories": [],
34 | "sortid": "FB039968",
35 | "firstitemmsec": "1252975023032"
36 | },
37 | {
38 | "id": "feed/http://www.amazon.com/rss/new-releases/books/5/ref=pd_nr_rss_link",
39 | "title": "Amazon.com: Hot New Releases in Books > Computers & Internet",
40 | "categories": [],
41 | "sortid": "77E40AE6",
42 | "firstitemmsec": "1262159994543"
43 | },
44 | {
45 | "id": "feed/http://blog.cartercole.com/feeds/posts/default?orderby=updated",
46 | "title": "Carter Cole's Blog",
47 | "categories": [],
48 | "sortid": "0B6626E4",
49 | "firstitemmsec": "1280455399985"
50 | },
51 | {
52 | "id": "feed/http://feed.feedsky.com/CWUfeed",
53 | "title": "China Web Updates",
54 | "categories": [],
55 | "sortid": "E53C4858",
56 | "firstitemmsec": "1252598589828"
57 | },
58 | {
59 | "id": "feed/http://feeds.feedburner.com/cwr",
60 | "title": "China Web2.0 Review",
61 | "categories": [],
62 | "sortid": "2EF2197E",
63 | "firstitemmsec": "1239983501546"
64 | }
65 | ]
66 | }
67 |
68 | ## Edit Subscription
69 |
70 | POST /subscription/edit
71 |
72 | ### Authentication
73 |
74 | Required
75 |
76 | ### Request Parameters
77 |
78 | ck
79 | : `timestamp`: Current timestamp to prevent caching issues.
80 |
81 | client
82 | : `application name`: String that identifies the client used.
83 |
84 | ### Post Parameters
85 |
86 | s
87 | : `stream id`: The feed that is to be edited.
88 |
89 | T
90 | : `token`: Authentication token
91 |
92 | ac
93 | : Action to perform: `subscribe`, `unsubscribe`, or `edit`.
94 |
95 | a
96 | : `tag`: Add a new tag
97 |
98 | r
99 | : `tag`: Remove an existing tag
100 |
101 | t
102 | : `feed title`
103 |
104 | ### Response
105 |
106 | OK
107 |
108 | ## Quick Add Subscription
109 |
110 | This endpoint allows the quick subscription of a feed where no title or tags are specified.
111 |
112 | POST /subscription/quickadd
113 |
114 | ### Authentication
115 |
116 | Required
117 |
118 | ### Request Parameters
119 |
120 | ck
121 | : `timestamp`: Current timestamp to prevent caching issues.
122 |
123 | client
124 | : `application name`: String that identifies the client used.
125 |
126 | ### Post Parameters
127 |
128 | quickadd
129 | : `stream id`: The feed that is to be subscribed to.
130 |
131 | T
132 | : `token`: Authentication token
133 |
134 | ### Response
135 |
136 | OK
137 |
138 | ## Export Subscriptions
139 |
140 | GET /subscriptions/export
141 |
142 | ### Authentication
143 |
144 | Required
145 |
146 | ### Response
147 |
148 | OPML file of all subscriptions belonging to the authenticated user.
149 |
150 | ## Check Subscribed Status
151 |
152 | Check if a user is subscribed to a specific feed already.
153 |
154 | GET /subscribed
155 |
156 | ### Request Parameters
157 |
158 | s
159 | : `stream id`: The feed that is to be checked.
160 |
161 | ### Response
162 |
163 | Boolean
164 |
--------------------------------------------------------------------------------
/content/resources.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Resources
3 | ---
4 |
5 | # Resources
6 |
7 | * TOC
8 | {:toc}
9 |
10 | This page is a list of resources pertaining to exsiting and past RSS APIs and any other sources of information that may aid in the creation of the Open Reader API.
11 |
12 | APIs listed are **not** Open Reader API compliant as the specification is still being developed. A separate page for services that fully support the Open Reader API will be created once more work is done.
13 |
14 | ## RSS Services & RSS API Documentation
15 |
16 | [BirdReader API](https://github.com/glynnbird/birdreader/blob/master/API.md)
17 | : BirdReader not only has a web interface, it also has a clean(ish) RESTful API too, which you can use to build your own front-ends
18 |
19 | [Feedbin API](https://github.com/feedbin/feedbin-api)
20 | : [Feedbin](https://feedbin.me)'s REST-style API.
21 |
22 | [Fever API Public Beta](http://www.feedafever.com/api)
23 | : This API is in public beta and currently supports basic syncing and consuming of content.
24 |
25 | [NewsBlur API](http://www.newsblur.com/api)
26 | : "We're quite pleased to point out that this entire API is open-source, including the implementation of the endpoints."
27 |
28 | [Tiny Tiny RSS Json API](http://tt-rss.org/redmine/projects/tt-rss/wiki/JsonApiReference)
29 | : Since version 1.4.0, Tiny Tiny RSS has an API. Terminal based [Newsbeuter](https://github.com/akrennmair/newsbeuter) supports it.
30 |
31 | [SAF-protocol](http://saf-protocol.org)
32 | : The Serverside Aggregated Feeds (SAF) protocol allow multiple lightweight clients to access feeds of data that are checked and fetched by a central server. Or in human speak: like Google Reader, but with a standardized protocol.
33 |
34 | [Selfoss Restful API](https://github.com/SSilence/selfoss/wiki/Restful-API-for-Apps-or-any-other-external-access)
35 | : [selfoss](http://selfoss.aditu.de) offers an restful JSON API for applications. If you are a programmer of a mobile app, plugin or any other system which wants to access selfoss then this is your place.
36 |
37 | ## Unofficial Google Reader Documentation
38 |
39 | [undocgoogle 1.0 documentation](http://undoc.in) by Daniel
40 | : This information is unofficial and subject to change. Some resource about Google Reader api is available, but unfortunately a lot of the information in that document is outdated, so I trying to gather these info and update it.
41 |
42 | [Pyrfeed's Documentation](https://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI)
43 | : This document is totally unofficial. You should not rely on anything on this document is you need an exact information. This document has been made mainly by reverse engeneering the protocol.
44 |
45 | [Nick Bradbury's Documentation](http://inessential.com/2013/03/14/google_reader_api_documentation)
46 | : It’s from 2009. Parts of it are surely out-of-date (authentication, for sure), and it was written with the expectation that the reader is familiar with NewsGator syncing.
47 |
48 | [google-reader-api](http://code.google.com/p/google-reader-api/w/list) by Mihai Parparita
49 | : Hosts documentation and the issue tracker for the (unofficial) Google Reader API.
50 |
51 | ## Other Documentation
52 |
53 | [FeedSink API](/Open-Reader-API/resources/feedsink-api/) by Avi Flax of Arc90
54 | : "… some ideas for a feed syncing API. My firm and I have decided to drop it, so I thought I’d share my documentation ... just in case it might somehow prove even slightly useful."
55 |
56 | [Netvibes REST API](http://dev.netvibes.com/doc/api/rest)
57 | : [Netvibes](http://www.netvibes.com/)'s REST-style API. This isn't an RSS related API but listed here as an example of an API.
58 |
59 | ## Miscellaneous
60 |
61 | * [The RSS-Sync feed corpus](https://github.com/rss-sync/corpus) — examples of real feeds, some of which show problems, from around the web.
62 | * [Stupid Feed Tricks](http://inessential.com/2013/03/18/brians_stupid_feed_tricks) by Brian Reischl — documents some of the problems with feeds that exist that one may encounter when building an RSS client.
63 |
--------------------------------------------------------------------------------
/nanoc.yaml:
--------------------------------------------------------------------------------
1 | # A list of file extensions that nanoc will consider to be textual rather than
2 | # binary. If an item with an extension not in this list is found, the file
3 | # will be considered as binary.
4 | text_extensions: [ 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'sass', 'scss', 'txt', 'xhtml', 'xml' ]
5 |
6 | # The path to the directory where all generated files will be written to. This
7 | # can be an absolute path starting with a slash, but it can also be path
8 | # relative to the site directory.
9 | output_dir: output/Open-Reader-API/
10 |
11 | # A list of index filenames, i.e. names of files that will be served by a web
12 | # server when a directory is requested. Usually, index files are named
13 | # “index.html”, but depending on the web server, this may be something else,
14 | # such as “default.htm”. This list is used by nanoc to generate pretty URLs.
15 | index_filenames: [ 'index.html' ]
16 |
17 | # Whether or not to generate a diff of the compiled content when compiling a
18 | # site. The diff will contain the differences between the compiled content
19 | # before and after the last site compilation.
20 | enable_output_diff: false
21 |
22 | prune:
23 | # Whether to automatically remove files not managed by nanoc from the output
24 | # directory. For safety reasons, this is turned off by default.
25 | auto_prune: false
26 |
27 | # Which files and directories you want to exclude from pruning. If you version
28 | # your output directory, you should probably exclude VCS directories such as
29 | # .git, .svn etc.
30 | exclude: [ '.git', '.hg', '.svn', 'CVS' ]
31 |
32 | # The data sources where nanoc loads its data from. This is an array of
33 | # hashes; each array element represents a single data source. By default,
34 | # there is only a single data source that reads data from the “content/” and
35 | # “layout/” directories in the site directory.
36 | data_sources:
37 | -
38 | # The type is the identifier of the data source. By default, this will be
39 | # `filesystem_unified`.
40 | type: filesystem_unified
41 |
42 | # The path where items should be mounted (comparable to mount points in
43 | # Unix-like systems). This is “/” by default, meaning that items will have
44 | # “/” prefixed to their identifiers. If the items root were “/en/”
45 | # instead, an item at content/about.html would have an identifier of
46 | # “/en/about/” instead of just “/about/”.
47 | items_root: /
48 |
49 | # The path where layouts should be mounted. The layouts root behaves the
50 | # same as the items root, but applies to layouts rather than items.
51 | layouts_root: /
52 |
53 | # Whether to allow periods in identifiers. When turned off, everything
54 | # past the first period is considered to be the extension, and when
55 | # turned on, only the characters past the last period are considered to
56 | # be the extension. For example, a file named “content/about.html.erb”
57 | # will have the identifier “/about/” when turned off, but when turned on
58 | # it will become “/about.html/” instead.
59 | allow_periods_in_identifiers: false
60 |
61 | # Configuration for the “watch” command, which watches a site for changes and
62 | # recompiles if necessary.
63 | watcher:
64 | # A list of directories to watch for changes. When editing this, make sure
65 | # that the “output/” and “tmp/” directories are _not_ included in this list,
66 | # because recompiling the site will cause these directories to change, which
67 | # will cause the site to be recompiled, which will cause these directories
68 | # to change, which will cause the site to be recompiled again, and so on.
69 | dirs_to_watch: [ 'content', 'layouts', 'lib' ]
70 |
71 | # A list of single files to watch for changes. As mentioned above, don’t put
72 | # any files from the “output/” or “tmp/” directories in here.
73 | files_to_watch: [ 'nanoc.yaml', 'Rules' ]
74 |
75 | # When to send notifications (using Growl or notify-send).
76 | notify_on_compilation_success: true
77 | notify_on_compilation_failure: true
78 |
--------------------------------------------------------------------------------
/content/rssconsensus.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: The RSS Consensus
3 | ---
4 |
5 | # The RSS Consensus
6 |
7 | * TOC
8 | {:toc}
9 |
10 | ## A Letter to all Present and Future RSS Syncing Platforms
11 |
12 | First of congratulations! The shutdown of Google Reader has created the perfect opportunity for you to show off your talents and bring ([some would say][n1], desperately needed) new ideas and innovation to the realm of RSS. We are all very keen to see these ideas come to life.
13 |
14 | Right now you are probably preoccupied with pushing releases and keeping servers running smoothly. But I would nonetheless like to call your attention to the following issue: **The lack of consensus, among RSS Syncing Platforms, on a new common API format.**
15 |
16 | Google Reader was the de facto standard and was thus used as a platform for others to build upon[↬][n2]. With its upcoming shutdown, we are entering a fragmented market of feed-syncing platforms (I count at least half a dozen already). This puts us at the brink of war, e.g. a new [format war][n3] that, if it breaks out, will 1) take end-users as hostages, 2) discourage other developers from building upon the technology, and 3) erode any progress and innovation you might otherwise be bringing to the table.
17 |
18 | A format war would be devastating for the RSS ecosystem. We take it for granted now that content providers put up RSS feeds. But this is not a given. They will only do so long as it's worth their effort and it drives traffic.
19 |
20 | > The more people used Reader, the more attractive it was to have an RSS feed and to write posts in feed-friendly ways. And the more people provided RSS content and structured online interactions around the blogs that pass through RSS, the more attractive it became to be a part of that ecosystem. If you then pull away the product at the heart of that system, you end up causing significant disruption … [↬ Google's Google problem][n7]
21 |
22 | All this when we instead could share a common platform and could be:
23 |
24 | * Fighting for new users, not just the existing Google Reader users. *»Reader is (was?) for information junkies; not just tech nerds. This market totally exists and is weirdly under-served (and is possibly affluent)«*[↬][n9] as Chris Wetherell writes.
25 | * Convincing those many still, profoundly unaware of the great benefits of RSS, to come join in and experience. Experience benefits you and I enjoy everday. However it might require a [great deal of explaining][n10] to give RSS general consumer appeal.
26 | * Competing on creating great products, not on creating lock-ins.
27 |
28 | > … one of the main reasons why Google Reader could exist was because companies and entities with completely conflicting agendas came together to support RSS and other standards. Google, MoveableType, Blogger, WordPress, Flickr and several other web apps believed in creating RSS feeds for easy consumption. “In the end it helped the average users,” said Wetherell. [↬ Google Reader lived on borrowed time][n8]
29 |
30 | [It is not a winner-take-all market][n14], there will be many winners. I am inclined to say you can all be winners. That is if you [collaborate and grow the pie, not just split it][n11]. Let's do it. Let's [once again][n13] put aside differences and work together to share in one common platform:
31 |
32 | ### THE RSS CONSENSUS PLEDGE:
33 |
34 | 1. **One new feed-sync protocol**, based (or not) on the Google Reader API.
35 | * Open and 100% vendor neutral.
36 | * Implemented by everybody.
37 | * Cleanly and thoroughly specified.
38 | 2. **Further development of this protocol**.
39 | * Adding additional features.
40 | * Freely extensible by anybody.
41 |
42 | If you can support this, please [link to it](/Open-Reader-API/rssconsensus/) from your weblog or twitter account, with a few words as to why you support it. For further discussion and infomation on the technical aspects, please refer to [The RSS-Sync mailing list](http://lists.ranchero.com/listinfo.cgi/rss-sync-ranchero.com).
43 |
44 | Yours truly,
45 | *Fans of the RSS Consensus*
46 |
47 |
48 | [n1]: http://techcrunch.com/2013/03/17/good-riddance-google-reader/ "Good Riddance, Google Reader"
49 |
50 | [n2]: http://www.pastemagazine.com/articles/2013/03/the-sad-end-of-google-reader-and-what-it-says-abou.html "The Sad End of Google Reader and What it Says About The Music Business"
51 |
52 | [n3]: http://en.wikipedia.org/wiki/Format_war "VHS vs. Betamax; HD DVD vs. Blu-ray; RSS vs. ATOM."
53 |
54 | [n7]: http://www.economist.com/blogs/freeexchange/2013/03/utilities "Google's Google problem"
55 |
56 | [n8]: http://gigaom.com/2013/03/13/chris-wetherll-google-reader/ "Google Reader lived on borrowed time: creator Chris Wetherell reflects"
57 |
58 | [n9]: https://plus.google.com/101851274707291135260/posts/FipoiXvRaa3 "Wethrell on Google Reader"
59 |
60 | [n10]: http://techcrunch.com/2013/03/13/google-readers-death-is-proof-that-rss-always-suffered-from-lack-of-consumer-appeal/ "Google Reader’s Death Is Proof That RSS Always Suffered From Lack Of Consumer Appeal"
61 |
62 | [n11]: http://blogs.hbr.org/cs/2011/06/collaborate_to_grow_the_pie_no.html "Collaborate to Grow the Pie, Not Just Split It"
63 |
64 | [n13]: http://www.intertwingly.net/wiki/pie/RoadMap#head-74b5d46318b48115b07ab1a2e77cb22df987c284 "Support for The Atom Syndication Format and Publishing Protocol"
65 |
66 | [n14]: http://jonas.sekamane.com/2013/04/not-winner-take-all/ "Not Winner Take All"
67 |
68 | *****
69 |
70 | ## Supporters
71 |
72 | **Company / Product List** (Alphabetically):
73 |
74 | 1. [BazQux](http://blog.bazqux.com/2013/04/open-reader-api.html)
75 | 2. [FeedHQ](https://feedhq.org/blog/3-supporting-google-reader-api)
76 |
77 |
78 | **Name List:**
79 |
80 | If a name is linked, it should go to a permalink for the source of the quote. Add new names at the bottom of the page.
81 |
82 | 1. [Владимир Шабанов](http://blog.bazqux.com/2013/04/open-reader-api.html): "We need single open vendor neutral feed-sync API."
83 | 2. [Avi Flax](http://aviflax.com/post/i-support-the-rss-consensus-on-the-open-reader-api/): "I support The RSS Consensus on the Open Reader API because feeds are fantastic and we need a vibrant ecosystem."
84 | 3. [Bruno Renié](https://feedhq.org/blog/3-supporting-google-reader-api): "... it is becoming clear that APIs for feed syncing and reading need to be standardized. This would avoid duplicating work for both server and client developers. FeedHQ fully supports the Open Reader API initiative. We'll do our best to support the standard which will emerge because we think it's best for the end users."
85 |
86 |
--------------------------------------------------------------------------------
/content/resources/feedsink-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: FeedSink API
3 | ---
4 | # FeedSink API
5 |
6 | by Avi Flax of [Arc90](http://arc90.com)
7 |
8 | This document is copyright © 2013 Arc90, Inc. — but APIs themselves are not subject to copyright, so feel free to implement
9 | or reuse the actual API design for any purpose.
10 |
11 | This document is licensed to anyone to do anything with. No rights reserved.
12 |
13 | ## Authentication
14 | TBD
15 |
16 | ## Use Cases
17 |
18 | ### As a client, I was just launched, so I want to retrieve all the changes the user made since the last time I checked
19 |
20 | #### Request
21 |
22 | * Send a GET request to the resource “a user’s changes since {date-time}”
23 | * example path: `/users/{user-id}/changes-since-2013-03-18T21:34:22-04:00`
24 | * The request header `Accept` must be `application/json; schema=feedsink.user-changes.v.1.0`
25 |
26 | #### Success Response
27 |
28 | * The response header `Content-Type` will be `application/json; schema=feedsink.user-changes.v1.0`
29 |
30 | The response entity will be a JSON object which looks like this:
31 |
32 | {
33 | "feeds": {
34 | "added": [
35 | {
36 | uri: "http://aviflax.com/feed",
37 | name: "Avi’s Rad Blog",
38 | "tags": ["racing" "friends"]
39 | },
40 | {
41 | uri: "http://aviflax.com/feed",
42 | name: "Avi’s Rad Blog",
43 | "tags": ["racing" "friends"]
44 | }
45 | ],
46 | "deleted": [
47 | "http//feed.zombo.com/"
48 | ],
49 | "updated": [
50 | {
51 | uri: "http://aviflax.com/feed",
52 | name: "Avi’s Rad Blog",
53 | "tags": ["racing" "friends"]
54 | },
55 | {
56 | uri: "http://aviflax.com/feed",
57 | name: "Avi’s Rad Blog",
58 | "tags": ["racing" "friends"]
59 | }
60 | ]
61 | },
62 | "feed-entries": {
63 | "http://aviflax.com/feed": {
64 | "read": [
65 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
66 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
67 | ],
68 | "unread": [
69 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
70 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
71 | ],
72 | "starred": [
73 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
74 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
75 | ],
76 | "unstarred": [
77 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
78 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
79 | ]
80 | },
81 | "http://arc90.com/blog/entries?format=atom": {
82 | "read": [
83 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
84 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
85 | ],
86 | "unread": [
87 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
88 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
89 | ],
90 | "starred": [
91 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
92 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
93 | ],
94 | "unstarred": [
95 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
96 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
97 | ]
98 | }
99 | }
100 | }
101 |
102 |
103 | #### Error Responses
104 | TBD
105 |
106 |
107 | ### As a client, I just downloaded new entries from one of a user’s feeds, and I want to check whether any of them have been read already, so that I don’t show the user entries they already read
108 |
109 | #### Request
110 |
111 | * Send a GET request to the resource “a user’s read entries for feed {uri}”
112 | * example path: `/users/{user-id}/feeds/{uri}/entries/read`
113 | * The request header `Accept` must be `application/json; schema=feedsink.read-entries.v1.0`
114 |
115 | #### Success Response
116 |
117 | * The response header `Content-Type` will be `application/json; schema=feedsink.read-entries.v1.0`
118 |
119 | The response entity will be a JSON object which looks like this:
120 |
121 | {
122 | "read_entries": [
123 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
124 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
125 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
126 | ]
127 | }
128 |
129 |
130 | #### Error Responses
131 | TBD
132 |
133 |
134 | ### Set the read/unread/starred/unstarred status for entries from one of a user’s feeds
135 |
136 | * so their status is the preserved if they switch to a different device or app
137 |
138 | #### Request
139 |
140 | * Send a POST request to the resource “a user’s entries for feed {uri}”
141 | * example path: `/users/{user-id}/feeds/{uri}/entries`
142 | * The value of the request header `Content-Type` must be `application/json; schema=feedsink.statuschanges.v1.0`
143 |
144 | The request entity should be a JSON object which looks like this:
145 |
146 | {
147 | "read": [
148 | // tuples of entry_id and user_action_date
149 | [
150 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
151 | '2013-03-12T23:22:11-04:00'
152 | ],
153 | [
154 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
155 | '2013-03-12T23:22:11-04:00'
156 | ]
157 | ],
158 | "unread": [
159 | // tuples of entry_id and user_action_date
160 | [
161 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
162 | '2013-03-12T23:22:11-04:00'
163 | ],
164 | [
165 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
166 | '2013-03-12T23:22:11-04:00'
167 | ]
168 | ],
169 | "starred": [
170 | // tuples of entry_id and user_action_date
171 | [
172 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
173 | '2013-03-12T23:22:11-04:00'
174 | ],
175 | [
176 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
177 | '2013-03-12T23:22:11-04:00'
178 | ]
179 | ],
180 | "unstarred": [
181 | // tuples of entry_id and user_action_date
182 | [
183 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
184 | '2013-03-12T23:22:11-04:00'
185 | ],
186 | [
187 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
188 | '2013-03-12T23:22:11-04:00'
189 | ]
190 | ],
191 | }
192 |
193 |
194 | * The entity should not include all entries for the feed, rather only those which the client wishes to set the status of
195 | * `user_action_date` is included so the service can discard user actions which it recieves out of order.
196 | * The elements of the arrays `read` and `unread` are tuples rather than objects so as to minimize data size
197 | * mobile clients will be uploading this representation and it’s important to try to keep the uploads small
198 | * most (maybe all) HTTP client libraries don’t support compressing request entities
199 |
200 | #### Success Response
201 |
202 | * If the request is successful, you will receive either a 204 or 202 response
203 |
204 | #### Error Responses
205 | TBD
206 |
207 |
208 | ### Retrieve the list of all of a user’s feeds
209 |
210 | * Each feed should include a list of the N most recent read entries so they can be filtered out without making a ton of additional requests
211 |
212 | #### Request
213 |
214 | * Send a GET request to the resource “a user’s feeds”
215 | * example path: `/users/{user-id}/feeds`
216 | * The request header `Accept` must be `application/json; schema=feedsink.feed-list.v1.0`
217 |
218 | #### Success Response
219 |
220 | The response header `Content-Type` will be `application/json; schema=feedsink.feed-list.v1.0`
221 |
222 | The response entity will be a JSON object which looks like this:
223 |
224 | {
225 | "feeds": [
226 | {
227 | "uri": "http://aviflax.com/feed",
228 | "name": "Avi’s Silly Blog",
229 | "tags": ["favorites"],
230 | "read_entries": [
231 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
232 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
233 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
234 | ]
235 | },
236 | {
237 | "uri": "http://arc90.com/feed",
238 | "name": "Arc90 Blog",
239 | "tags": ["river", "arc"],
240 | "read_entries": [
241 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
242 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a',
243 | 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
244 | ]
245 | }
246 | ]
247 | }
248 |
249 | #### Error Responses
250 | TBD
251 |
252 |
253 | ### Add a feed to a user’s list of feeds
254 |
255 | #### Request
256 |
257 | * Send a PUT request to the resource “the feed {uri} of a user”
258 | * example path: `/users/{user-id}/feeds/{feed-uri}`
259 | * The request header `Content-Type` must be `application/json; schema=feedsink.feed.v1.0`
260 |
261 | The request entity should be a JSON object which looks like this:
262 |
263 | {
264 | "uri": "http://aviflax.com/feed",
265 | "name": "Avi’s Silly Blog",
266 | "tags": ["river", "arc/staff"],
267 | }
268 |
269 | Please note that a tag may contain `/` to denote that it is heirarchical.
270 |
271 | #### Success Response
272 |
273 | * The status code will be either 201 or 202
274 | * There will be no entity
275 |
276 | #### Error Responses
277 | * TBD
278 |
279 |
280 | ### Remove a feed from a user’s list of feeds
281 |
282 | #### Request
283 |
284 | * Send a DELETE request to the resource “the feed {uri} of user {user-id}”
285 | * example path: `/users/{user-id}/feeds/{feed-uri}`
286 | * The request header [`If-Unmodified-Since`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28) must be included
287 | * the value should be the date-time at which the user initiated the deletion
288 |
289 | #### Success Response
290 |
291 | * The status code will be either 202 or 204
292 | * There will be no entity
293 |
294 | #### Error Responses
295 | TBD
296 |
297 |
298 | ### Add multiple feeds to a user’s list of feeds
299 |
300 | * This is intended for the “import” use-case wherein a user is adopting the API for the first time, and wishes to import their feed list from another application or service
301 | * Clients are recommended to use this method only when they have a significant number of feeds to add at once
302 | * Otherwise, PUT requests to the resource “the feed {uri} of user {user-id}” are recommended, as they are idempotent and therefore less likely to result in an undesired outcome, as in the case of an error they can be safely resent
303 |
304 | #### Request
305 |
306 | * Send a POST request to the resouce “a user’s feeds”
307 | * example path: `/users/{user-id}/feeds`
308 | * The request header `Content-Type` must be `application/json; schema=feedsink.feed-list.v1.0`
309 |
310 | The request entity should be a JSON object which looks like this:
311 |
312 | {
313 | "feeds": [
314 | {
315 | "uri": "http://aviflax.com/feed",
316 | "name": "Avi’s Silly Blog",
317 | "tags": ["favorites"]
318 | },
319 | {
320 | "uri": "http://arc90.com/feed",
321 | "name": "Arc90 Blog",
322 | "tags": ["river", "arc"]
323 | }
324 | ]
325 | }
326 |
327 | #### Success Response
328 |
329 | * The status code will be either 200, 202, or 204
330 | * There will be no entity
331 |
332 | #### Error Responses
333 | * TBD
334 |
335 |
336 | ### Change the name or tags of a user’s feed
337 |
338 | #### Request
339 |
340 | * Send a PUT request to the resource “the feed {uri} of a user”
341 | * example path: `/users/{user-id}/feeds/{feed-uri}`
342 | * The request header `Content-Type` must be `application/json; schema=feedsink.feed.v1.0`
343 | * The request header [`If-Unmodified-Since`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28) must be included
344 | * the value should be the date-time at which the user entered the changes
345 |
346 | The request entity should be a JSON object which looks like this:
347 |
348 | {
349 | "uri": "http://aviflax.com/feed",
350 | "name": "Avi’s Silly Blog",
351 | "tags": ["river", "arc/staff"],
352 | }
353 |
354 | * A tag may contain `/` to denote that it is heirarchical
355 | * If the value of `uri` does not match the path segment `feed-uri` of the target resource URI path, the request will be rejected
356 |
357 | #### Success Response
358 |
359 | * The status code will be 200 or 202
360 | * There will be no entity
361 |
362 | #### Error Responses
363 | TBD
364 |
--------------------------------------------------------------------------------