├── .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 |
13 |
Open Reader API
14 |
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 | --------------------------------------------------------------------------------