@@ -11,7 +11,13 @@ kick the tires than [cURL][curl].
1111
1212###Hello World
1313
14- Let start by` GET ` ting Chris Wanstrath's GitHub profile:
14+ Let's start by testing our setup:
15+
16+ curl https://api.github.com/zen
17+
18+ Keep it logically awesome.
19+
20+ Next, let's` GET ` Chris Wanstrath's GitHub profile:
1521
1622# GET /users/defunkt
1723curl https://api.github.com/users/defunkt
@@ -93,7 +99,7 @@ earlier, you should see a `plan` object on the response:
9399
94100While convenient, Basic Auth isn't ideal because you shouldn't give your GitHub
95101username and password to anyone. Applications that need to read or write
96- private information using the API on behalf of another user should use[ OAuth] [ oauth ] .
102+ private information using the API on behalf of another user should use[ OAuth] [ oauth ] .
97103
98104Instead of usernames and passwords, OAuth uses_ tokens_ . Tokens provide two big
99105features:
@@ -105,13 +111,43 @@ authorize a third party app.
105111Normally, tokens are created via a[ web flow] [ webflow ] , where third party
106112applications send users to GitHub to log in and authorize their application and
107113GitHub redirects the user back to the third party application. You don't need
108- to set up the entire web flow to begin working with OAuth tokens. The[ Authorizations API] [ authorizations api ]
114+ to set up the entire web flow to begin working with OAuth tokens. The[ Authorizations API] [ authorizations api ]
109115makes it simple to use Basic Auth to create an OAuth token.
110116
111117curl -i -u pengwynn \
112118 -d '{"scopes": ["repo"]}' \
113119 https://api.github.com/authorizations
114120
121+ HTTP/1.1 201 Created
122+ Server: nginx/1.0.14
123+ Date: Wed, 14 Nov 2012 14:04:24 GMT
124+ Content-Type: application/json; charset=utf-8
125+ Connection: keep-alive
126+ Status: 201 Created
127+ Cache-Control: max-age=0, private, must-revalidate
128+ X-Content-Type-Options: nosniff
129+ ETag: "f6a0ce8bac4559f2a578afd9dad51f95"
130+ X-GitHub-Media-Type: github.beta
131+ Location: https://api.github.com/authorizations/2
132+ Content-Length: 384
133+
134+ {
135+ "scopes": [
136+ "repo"
137+ ],
138+ "token": "5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4",
139+ "updated_at": "2012-11-14T14:04:24Z",
140+ "url": "https://api.github.com/authorizations/2",
141+ "app": {
142+ "url": "http://developer.github.com/v3/oauth/#oauth-authorizations-api",
143+ "name": "GitHub API"
144+ },
145+ "created_at": "2012-11-14T14:04:24Z",
146+ "note_url": null,
147+ "id": 2,
148+ "note": null
149+ }
150+
115151There's a lot going on in this one little call, so let's break it down. First,
116152the` -d ` flag indicates we're doing a` POST ` , using the
117153` application/x-www-form-urlencoded ` content type. All` POST ` requests to the
@@ -124,19 +160,252 @@ we're setting up the token with _repo_ access, the most permissive scope in the
124160GitHub API, allowing access to read and write to private repositories. See[ the
125161docs] [ scopes ] for a full list of scopes.
126162
163+ The` 201 ` status tells us the call was successful and the JSON returned
164+ contains the details of our new OAuth token. Now we can use the forty character
165+ ` token ` instead of username and password in the rest of our examples. Let's
166+ grab our own user info again, using OAuth this time:
167+
168+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
169+ https://api.github.com/user
170+
171+ A quick note about tokens.** Treat OAuth tokens like passwords.**
172+ Don't share them with other users or store them in insecure places. The tokens
173+ in these examples are fake and the names have been changed to protect the
174+ innocent.
175+
176+ Now that we've got the hang of making authenticated calls, let's move along to
177+ the[ Repositories API] [ repos-api ] .
178+
127179##Repositories
128180
129- ##Commits
181+ Most any meaningful use of the GitHub API will involve some level of Repository
182+ information. We can` GET ` repository details in the same way we fetched user
183+ details earlier:
184+
185+ curl -i https://api.github.com/repos/twitter/bootstrap
186+
187+ In the same way, we can view repositories for the authenticated user:
188+
189+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
190+ https://api.github.com/user/repos
191+
192+ ... list repositories for another user:
193+
194+
195+ curl -i https://api.github.com/users/technoweenie/repos
196+
197+ ... or list repositories for an organization:
198+
199+ curl -i https://api.github.com/orgs/github/repos
200+
201+ The information returned from these calls will depend on how we authenticate:
202+
203+ * Using Basic Auth, everything the user has access to see on github.com
204+ * Using OAuth, private repositories are only returned if the OAuth token contains 'repo' scope.
205+
206+ As the[ docs] [ repos-api ] indicate, these methods take a` type ` parameter that
207+ can filter the repositories returned based on what type of access the user has
208+ for the repository. In this way we can fetch only directly-owned repositories,
209+ organization repositories, or repositories the user collaborates on via a
210+ team.
211+
212+ curl -i "https://api.github.com/users/technoweenie/repos?type=owner"
213+
214+ In this example, we can grab only those repositories that Rick owns, not the
215+ ones on which he collaborates. Note the quoted URL above. Depending on your
216+ shell setup, cURL sometimes requires a quoted URL or else it ignores the
217+ querystring.
218+
219+ ###Create a repository
220+
221+ Fetching information for existing repositories is a common use case, but the
222+ GitHub API supports creating new repositories as well. To create a repository
223+ we need to` POST ` some JSON containing the details and configuration options.
224+
225+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
226+ -d '{ \
227+ "name": "blog", \
228+ "auto_init": true, \
229+ "private": true, \
230+ "gitignore_template": "nanoc" \
231+ }' \
232+ https://api.github.com/user/repos
233+
234+ In this minimal example, we create a new repository for our blog (to be served
235+ on[ GitHub Pages] [ pages ] perhaps). Though the blog will be public, we've made
236+ the repository private. In this single step, we'll also init it with
237+ a README and a[ nanoc] [ nanoc ] -flavored[ .gitignore template] [ gitignore
238+ templates] .
130239
131- ###Paginate
240+ The resulting repository will be found at `https://github.com/ <your
241+ usrname>/blog`. To create a repository under an organization for which you're
242+ an owner, just change the API method from` /user/repos ` to `/orgs/{org
243+ name}/repos`.
132244
133- ## Star a repo
245+ Let's fetch our newly created repository:
134246
135- ##Follow a user
247+ curl -i https://api.github.com/pengwynn/blog
248+
249+ HTTP/1.1 404 Not Found
250+
251+ {
252+ "message": "Not Found"
253+ }
254+
255+ Oh noes! Where did it go? Since we created the repository as_ private_ , we need
256+ to authenticate in order to see it. If you're a grizzled HTTP user, you might
257+ expect a` 403 ` instead. Since we don't want to leak information about private
258+ repositories, the GitHub API returns a` 404 ` instead as if to say "we can
259+ neither confirm nor deny the existence of this repository."
260+
261+ ##Issues
262+
263+ The UI for Issues on GitHub aims to provide 'just enough' workflow while
264+ staying out of your way. With the GitHub[ Issues API] [ issues-api ] , you can pull
265+ data out or create issues from other tools to create a workflow that works for
266+ your team.
267+
268+ Just like github.com, the API provides a few methods to view issues for the
269+ authenticated user. To see all your issues,` GET /issues ` :
270+
271+
272+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
273+ https://api.github.com/issues
274+
275+ To get only the issues under one of your GitHub organizations, `GET
276+ /orgs/{org}/issues`:
277+
278+
279+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
280+ https://api.github.com/orgs/rails/issues
281+
282+ We can also get all the issues under a single repository:
283+
284+ curl -i https://api.github.com/repos/rails/rails/issues
285+
286+ ###Pagination
287+
288+ A project the size of Rails has thousands of issues. We'll need to paginate,
289+ making multiple API calls to get the data. Let's repeat that last call, this
290+ time taking note of the response headers:
291+
292+ curl -i https://api.github.com/repos/rails/rails/issues
293+
294+ HTTP/1.1 200 OK
295+
296+ Link: <https://api.github.com/repos/rails/rails/issues?page=2>; rel="next",
297+ <https://api.github.com/repos/rails/rails/issues?page=14>; rel="last"
298+
299+ The[ ` Link ` header] [ link-header ] provides a way for a response to link to
300+ external resources, in this case additional pages of data. Since our call found
301+ more than thirty issues (the default page size), the API tells us where we can
302+ find the next page and the last page of results.
303+
304+ ###Creating an issue
305+
306+ Now that we've seen how to paginate lsits of issues, let's create an issue from
307+ the API.
308+
309+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
310+ -d '{ \
311+ "title": "New logo", \
312+ "body": "We should have one", \
313+ "labels": ["design"] \
314+ }' \
315+ https://api.github.com/repos/pengwynn/api-sandbox/issues
316+
317+ To create an issue, we need to be authenticated so we pass an
318+ OAuth token in the header. We pass the title, body, and labels in the JSON
319+ body to the` /issues ` path underneath the repository in which we want to create
320+ the issue.
321+
322+ HTTP/1.1 201 Created
323+ Location: https://api.github.com/repos/pengwynn/api-sandbox/issues/17
324+ X-RateLimit-Limit: 5000
325+
326+ {
327+ "pull_request": {
328+ "patch_url": null,
329+ "html_url": null,
330+ "diff_url": null
331+ },
332+ "created_at": "2012-11-14T15:25:33Z",
333+ "comments": 0,
334+ "milestone": null,
335+ "title": "New logo",
336+ "body": "We should have one",
337+ "user": {
338+ "login": "pengwynn",
339+ "gravatar_id": "7e19cd5486b5d6dc1ef90e671ba52ae0",
340+ "avatar_url": "https://secure.gravatar.com/avatar/7e19cd5486b5d6dc1ef90e671ba52ae0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png",
341+ "id": 865,
342+ "url": "https://api.github.com/users/pengwynn"
343+ },
344+ "closed_at": null,
345+ "updated_at": "2012-11-14T15:25:33Z",
346+ "number": 17,
347+ "closed_by": null,
348+ "html_url": "https://github.com/pengwynn/api-sandbox/issues/17",
349+ "labels": [
350+ {
351+ "color": "ededed",
352+ "name": "design",
353+ "url": "https://api.github.com/repos/pengwynn/api-sandbox/labels/design"
354+ }
355+ ],
356+ "id": 8356941,
357+ "assignee": null,
358+ "state": "open",
359+ "url": "https://api.github.com/repos/pengwynn/api-sandbox/issues/17"
360+ }
361+
362+ The response gives us a couple of pointers to the newly created issue, both in
363+ the` Location ` response header and the` url ` field of the JSON response.
364+
365+ ###Convert an issue to a Pull Request
366+
367+ GitHub moves fast and the API tries to keep pace, but there are some things you
368+ can do with the API that you can't do on github.com. Using the API, you can
369+ turn an issue into a Pull Request. Let's convert the issue we just created
370+
371+
372+ curl -i -H 'Authorization: token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4' \
373+ -d '{ \
374+ "issue": 17, \
375+ "head": "new-feature", \
376+ "base": "master" \
377+ }' \
378+ https://api.github.com/repos/pengwynn/api-sandbox/pulls
379+
380+ Using this shorthand, the details for the pull request title and body are taken
381+ from the issue we provide.
136382
137383##Conditional requests
138384
139- ##JSON-P
385+ A big part of being a good API citizen is respecting rate limits and caching
386+ information that does not change. The API supports[ conditional
387+ requests] [ conditional-requests ] and helps you do the right thing. Consider the
388+ first call we made to get@defunkt 's profile:
389+
390+ curl -i https://api.github.com/users/defunkt
391+
392+ In addition to the JSON body, take note of the HTTP status code of` 200 ` and
393+ the` ETag ` header:
394+
395+ HTTP/1.1 200 OK
396+ ETag: "bfd85cbf23ac0b0c8a29bee02e7117c6"
397+
398+ The ETag is a fingerprint of the response. If we pass that on subsequent calls,
399+ we can tell the API to give us the resource again, only if it has changed:
400+
401+ curl -i -H 'If-None-Match: "644b5b0155e6404a9cc4bd9d8b1ae730"' \
402+ https://api.github.com/users/defunkt
403+
404+ HTTP/1.1 304 OK
405+
406+ The` 304 ` status indicates that the resource hasn't changed since the last time
407+ we asked for it and the response will contain no body. As a bonus,` 304 `
408+ responses don't count against your[ rate limit] [ rate-limiting ] .
140409
141410
142411
@@ -147,3 +416,11 @@ docs][scopes] for a full list of scopes.
147416[ webflow ] :http://developer.github.com/v3/oauth/#web-application-flow
148417[ authorizations api ] :http://developer.github.com/v3/oauth/#oauth-authorizations-api
149418[ scopes ] :http://developer.github.com/v3/oauth/#scopes
419+ [ repos-api ] :http://developer.github.com/v3/repos/
420+ [ pages ] :http://pages.github.com
421+ [ nanoc ] :http://nanoc.stoneship.org/
422+ [ gitignore templates ] :https://github.com/github/gitignore
423+ [ issues-api ] :http://developer.github.com/v3/issues/
424+ [ link-header ] :http://www.w3.org/wiki/LinkHeader
425+ [ conditional-requests ] :http://developer.github.com/v3/#conditional-requests
426+ [ rate-limiting ] :http://developer.github.com/v3/#rate-limiting