Table of Contents
Overview
This page contains information on getting access to your Jawbone UP data using their undocumented JSON API. This API was discovered by sniffing traffic between the iPhone/Jawbone UP applications and Jawbone's servers. An official API is supposedly available to partners and in the works. Until that time this method could be used, however there is no gaurantee on how long this approach will work (UP may disable this ability or change their protocol/schema at any time). This information should be used for gaining access to your own data and saving for your own use. This should by no means be used in any way that would violate the Jawbone UP TOS/AUP. Please note the information listed here is a work in progress and is by no means accurate (use at your own risk!). Have fun :)
High-level API Summary
Although the Jawbone UP requires the iPhone app to sync and there is no web-based interface, there is a server-side dependency for saving all synchronized data and fetching information on daily activity, workouts, sleep data, food, goals, searches, and social interactions. The API is used internally by the Jawbone UP application and interacts with the server (https://jawbone.com) over HTTPS/SSL and returns data in JSON format. The API appears to be broken down into the following categories:
- Users (Most features under the Profile Tab)
- Getting settings and profile info
- Aliases (not sure what this is for yet)
- Feeds (social feeds that contain workout, sleep, food, and other data)
- Inbox (messages from other team members?)
- Data sync
- Used by Sync feature to POST data to the Jawbone servers
- App also communicates battery level info
- Meals, Sleeps, Workouts, Health Credits (?)
- Service
- Gets Manifest (.zip file) with resources used by the app (Javascript, images, css, language resource bundles, etc.)
- Aliases
- TBD - need to research this more
- Search
- Queries for challenges and other team members
- Workouts
- A snapshot of the recorded workout
- Sleeps
- A snapshot of the recorded sleep
Timestamps
Most of the JSON requests/responses deal with times in epoch seconds (e.g. 1322353872). A quick cheat on the *nix side is to run 'date -d @1322353872' to get a timestamp in human readable format "
Sat Nov 26 16:31:12 PST 2011". Time offsets (e.g. time_offset) are in minutes. -28800 = (28,800/3,600) = -8 Hours (PST).
Notable HTTP Headers
The following HTTP headers appear to be in most requests. With the exception of x-nudge-token (optional since it can be passed in the query string), none of the other values appear to be validated:
User-Agent |
Nudge/1.3.1 CFNetwork/548.0.4 Darwin/11.0.0 |
x-nudge-token |
2QjnIfD_7a29Vdty-r4IL93)ldkqD7eiDll1hbolxDPavesPCdpCVfqWjrR6SUCtDGB-ZQsWnAT-dS4BO5qefyGZxC-0Luw-TPSVv_r1IClL4VdyyybDCLDtaoVXHCCunIbb8gLtV142rO9JFK1rH88okcnzTn0GNrXktiJDoN85YmFR9apO-6ogLc5cntZwvWMKEtEzxs |
x-nudge-platform |
iPhone 4; 5.0.1 |
x-nudge-request-id |
1322427258 (appears to increment each request) |
x-nudge-device-id |
1c35e728b7d0d36eb400f5621efb3ba2ca20xyzc |
JawBone App
I thought it was interesting that the UP app was making a number of requests to localhost (on the iPhone), port 8000. This is probably a web server that serves the files from the manifest.zip. Directories in that .zip:
|-- css
| |-- common
| `-- nudge
| `-- mobile
| `-- nom
|-- images
| |-- common
| | `-- facebook
| |-- nudge
| `-- up
|-- js
| |-- common
| |-- jquery
| |-- nudge
| | |-- bindings
| | |-- mobile
| | `-- nom
| `-- user
|-- nudge
| `-- mobile
|-- res
| `-- lang
| `-- load
`-- res_de
`-- lang
`-- load
Authentication
Each API request needs to be validated and requires that a secure token be passed. This token can be retrieved by performing a login request:
Request
METHOD |
POST |
URL |
https://jawbone.com/user/signin/login |
INPUT PARAMS |
[email protected] pwd=YourPassword service=nudge |
Response
{ "newly" : false , "rc" : 0 , "token" : "2QjnIfD_7a29Vdty-r4IL93)ldkqD7eiDll1hbolxDPavesPCdpCVfqWjrR6SUCtDGB-ZQsWnAT-dS4BO5qefyGZxC-0Luw-TPSVv_r1IClL4VdyyybDCLDtaoVXHCCunIbb8gLtV142rO9JFK1rH88okcnzTn0GNrXktiJDoN85YmFR9apO-6ogLc5cntZwvWMKEtEzxs" , "user" : { "data_1" : 0 , "data_2" : 0 , "first" : "First" , "flags" : 0 , "href" : "/nudge/profile?uid=1234567" , "image" : "" , "last" : "Last" , "mobile_href" : "/nudge/mobile/profile#qYTkyeUXXXX" , "name" : "First Last" , "text_1" : "" , "time_created" : 1322353872 , "time_removed" : 0 , "type" : 0 , "uid" : 1234567 , "xid" : "qYTkyeUXXXX" } } |
This token value is used to either populate the x-nudge-token HTTP header, or the _token query string parameter. It appears that if you don't populate the HTTP header, you can alternatively populate the query string value and the request will be processed OK.
Unauthorized Access
Any invalid requests (requesting with an invalid token or a user resource that you don't have access to) results in the following error:
{ "meta" : { "error_type" : "authentication_error" , "message" : "Unauthorized" , "code" : 401 , "error_detail" : "You must be logged in to perform that action" }, "data" : {}} |
Feed Summary
The following request is issued each time the Feed tab is viewed. This tab contains high-level info and a graphic for your Workouts, Sleep, and Food.
Request
METHOD |
GET |
URL |
https://jawbone.com/nudge/api/users/@me/social |
INPUT PARAMS |
after=null limit=30 _token=<your auth token> |
Response
{ "data" : { "feed" : [ { "activity_xid" : "OFBeT_bTXN0" , "duration" : 4659 , "image" : "/mod/workout_340x104/nudge/api/workouts/N6Tz4XoJDMo/image" , "is_completed" : 1 , "km" : 3.672 , "reaction" : 2 , "steps" : 4483 , "subtitle" : "First added 4483 steps" , "time_created" : 1322421880 , "time_updated" : 1322421880 , "title" : "1h 17m Workout" , "type" : "workout" , "user" : { "first" : "First" , "image" : "" , "last" : "Last" , "name" : "First Last" , "type" : "user" , "xid" : "qYTkyeUXXXX" }, "xid" : "N6Tz4XoJDMo" }, { "activity_xid" : "sxTVFCZwkqs" , "awake" : 801 , "duration" : 28101 , "image" : "/mod/sleep_340x104/nudge/api/sleeps/-QXJrBASWvo/image" , "quality" : 91 , "subtitle" : "First's sleep quality was 91" , "time_created" : 1322410838 , "time_updated" : 1322410838 , "title" : "Slept for 7:35" , "type" : "sleep" , "user" : { "first" : "First" , "image" : "" , "last" : "Last" , "name" : "First Last" , "type" : "user" , "xid" : "qYTkyeUXXXX" }, "xid" : "-QXJrBASWvo" }, { "activity_xid" : "uMH7MKQvBk4" , "image" : "/user/image/i/4ed1a7f238c04b359718e39f_GD7LW_3xtJc_132236286634_6401806_image.jpg" , "note" : "Wine" , "place_name" : "" , "reaction" : 2 , "subtitle" : "First felt OK" , "time_created" : 1322362848 , "time_updated" : 1322362848 , "title" : "Wine" , "type" : "meal" , "user" : { "first" : "First" , "image" : "" , "last" : "Last" , "name" : "First Last" , "type" : "user" , "xid" : "qYTkyeUXXXX" }, "xid" : "GD7LW_3xtJc" }, { "activity_xid" : "W_1oE95qi9M" , "duration" : 2712 , "image" : "/mod/workout_340x104/nudge/api/workouts/2xK2B_mW6Vs/image" , "is_completed" : 1 , "km" : 1.022 , "reaction" : null , "steps" : 1293 , "subtitle" : "First added 1293 steps" , "time_created" : 1322356451 , "time_updated" : 1322356451 , "title" : "45m 12s Workout" , "type" : "workout" , "user" : { "first" : "First" , "image" : "" , "last" : "Last" , "name" : "First Last" , "type" : "user" , "xid" : "qYTkyeUXXXX" }, "xid" : "2xK2B_mW6Vs" } ] }, "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |
Daily Summary (Activity + Sleep + Food) Data
The following request is issued when the Me tab is selected (the Today screen).
Request
METHOD |
GET |
URL |
https://jawbone.com/nudge/api/users/qYTkyeUXXXX/healthCredits |
INPUT PARAMS |
date=2011-11-27 timezone=-28800 move_goal=0 sleep_goal=0 eat_goal=0 check_levels=1 _token=<your auth token> |
Response
{ "data" : { "date" : "2011-11-27T00:00:00-08:00" , "eat" : 0 , "eat_goal" : 200 , "eat_percent" : 0 , "eat_summary" : { "meals" : [] }, "meal_photos" : [], "meal_xids" : [], "move" : 5052 , "move_goal" : 5000 , "move_kilometers" : 4.132 , "move_percent" : 101 , "move_seconds" : 4968 , "move_steps" : 5052 , "move_summary" : { "move_calories" : 396.325954 , "move_kilometers" : 4.132 , "move_seconds" : 4968 , "move_steps" : 5052 , "workouts" : [ { "completed" : 1 , "details" : { "calories" : 358.20608 , "duration" : 3.672 , "duration_type" : 2 , "steps" : 4483 , "time" : 4659 }, "reaction" : 2 , "route" : "" , "sub_type" : 0 , "time_created" : 1322421880 , "time_removed" : 0 , "title" : "1h 17m Workout" , "xid" : "N6Tz4XoJDMo" } ] }, "sleep" : 27300 , "sleep_goal" : 25200 , "sleep_percent" : 108 , "sleep_summary" : { "deep" : 13680 , "light" : 13620 , "sleeps" : [ { "details" : { "awake" : 801 , "body" : 0 , "deep" : 13680 , "duration" : 28101 , "light" : 13620 , "mind" : 0 , "quality" : 91 , "smart_alarm_fire" : 0 , "tz" : "GMT-0800" }, "time_created" : 1322382737 , "time_removed" : 0 , "title" : "Slept for 7:35" , "xid" : "-QXJrBASWvo" } ], "total" : 27300 }, "sleep_total" : 27300 , "total" : 32352 , "total_goal" : 30400 }, "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |
Detailed Activity Data
The following request is issued when an activity/workout graph is viewed. Intervals are 60 seconds apart. Note that a given profile ID (user_xid such as
https://jawbone.com/nudge/api/users/qYTkyeUkNXM/band
?) can also be substituted with @me
Request
METHOD |
GET |
URL |
https://jawbone.com/nudge/api/users/qYTkyeUXXXX/band |
INPUT PARAMS |
start_time=1322380800 (epoch) end_time=1322442000 (epoch) _token=<your auth token> |
Response
{ "data" : { "ticks" : [ { "value" : { "active_time" : 10 , "aerobic" : true , "bid" : "A756944628000F00" , "calories" : 1.486014 , "distance" : 16 , "speed" : 1.6 , "steps" : 19 , "time" : 1322381100 , "time_offset" : - 28800 , "type" : 0 } }, { "value" : { "active_time" : 17 , "aerobic" : true , "bid" : "A756944628000F00" , "calories" : 2.008668 , "distance" : 25 , "speed" : 1.470588 , "steps" : 31 , "time" : 1322381160 , "time_offset" : - 28800 , "type" : 0 } }, { "value" : { "active_time" : 13 , "aerobic" : true , "bid" : "A756944628000F00" , "calories" : 2.728756 , "distance" : 27 , "speed" : 2.076923 , "steps" : 30 , "time" : 1322427180 , "time_offset" : - 28800 , "type" : 0 } } ] }, "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUkNXM" } } |
Sleep Summary Data
The following request is issued when an sleep graph is viewed. Note that the xid returned for the sleep data can be used to get more detailed data (snapshot)
Request
METHOD |
GET |
URL |
https://jawbone.com/nudge/api/users/qYTkyeUXXXX/sleeps |
INPUT PARAMS |
start_time=1322380800 (epoch) end_time=1322442000 (epoch) limit=100 _token=<your auth token> |
Response
{ "data" : { "items" : [ { "details" : { "awake" : 801 , "body" : 0 , "deep" : 13680 , "duration" : 28101 , "light" : 13620 , "mind" : 0 , "quality" : 91 , "smart_alarm_fire" : 0 , "tz" : "GMT-0800" }, "links" : { "image" : "/nudge/api/sleeps/-QXJrBASWvo/image" , "self" : "/nudge/api/sleeps/-QXJrBASWvo" , "snapshot" : "/nudge/api/sleeps/-QXJrBASWvo/snapshot" }, "time_created" : 1322382737 , "title" : "Slept for 7:35" , "user_xid" : "qYTkyeUkNXM" , "xid" : "-QXJrBASWvo" } ], "links" : { "self" : "/nudge/api/users/qYTkyeUkNXM/sleeps" }, "size" : 1 }, "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |
Sleep Detailed Data (Snapshot)
The following request is issued when an sleep graph is viewed. Each sleep data entry is added whenever a state change happens (1, 2 or 3):
1 = awake
2 = deep
3 = light
Request
METHOD |
GET |
URL |
/nudge/api/sleeps/-QXJrBASWvo/snapshot |
INPUT PARAMS |
_token=<your auth token> |
Response
{ "data" : [ [ 1322382737 , 1 ], [ 1322383517 , 3 ], [ 1322387297 , 2 ], [ 1322388497 , 3 ], [ 1322390297 , 2 ], [ 1322392397 , 3 ], [ 1322395997 , 2 ], [ 1322397197 , 3 ], [ 1322399297 , 2 ], [ 1322402597 , 3 ], [ 1322403197 , 2 ], [ 1322405597 , 3 ], [ 1322406797 , 2 ], [ 1322408297 , 3 ], [ 1322408897 , 2 ], [ 1322410817 , 1 ] ], "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |
Workout Summary Data
The following request is issued when a workout graph is viewed. Note that the xid returned for the workout data can be used to get more detailed data (snapshot)
Request
METHOD |
GET |
URL |
https://jawbone.com/nudge/api/users/qYTkyeUXXXX/workouts |
INPUT PARAMS |
start_time=1322380800 (epoch) end_time=1322442000 (epoch) limit=100 _token=<your auth token> |
Response
{ "data" : { "items" : [ { "completed" : 1 , "contributions" : { "items" : [], "links" : { "self" : "/nudge/api/workouts/N6Tz4XoJDMo/contributions" }, "size" : 0 }, "details" : { "calories" : 358.20608 , "duration" : 3.672 , "duration_type" : 2 , "steps" : 4483 , "time" : 4659 }, "links" : { "cheers" : "/nudge/api/workouts/N6Tz4XoJDMo/cheers" , "contributions" : "/nudge/api/workouts/N6Tz4XoJDMo/contributions" , "image" : "/nudge/api/workouts/N6Tz4XoJDMo/image" , "reaction" : "/nudge/api/workouts/N6Tz4XoJDMo/reaction" , "route" : "/nudge/api/workouts/N6Tz4XoJDMo/route" , "self" : "/nudge/api/workouts/N6Tz4XoJDMo" , "snapshot" : "/nudge/api/workouts/N6Tz4XoJDMo/snapshot" , "title" : "/nudge/api/workouts/N6Tz4XoJDMo/title" }, "reaction" : 2 , "route" : "" , "sub_type" : 0 , "time_created" : 1322421880 , "time_removed" : 0 , "title" : "1h 17m Workout" , "user" : { "first" : "" , "href" : "/nudge/profile?uid=8606237" , "image" : "" , "last" : "" , "links" : { "challenges" : "/nudge/api/users/qYTkyeUkNXM/plans/today" , "meals" : "/nudge/api/users/qYTkyeUkNXM/meals" , "self" : "/nudge/api/users/qYTkyeUkNXM" , "workouts" : "/nudge/api/users/qYTkyeUkNXM/workouts" }, "mobile_href" : "/nudge/mobile/profile#qYTkyeUkNXM" , "name" : " " , "settings" : { "profile_privacy" : "friends" , "share_eat" : true , "share_move" : true , "share_sleep" : true }, "type" : "user" , "xid" : "qYTkyeUkNXM" }, "xid" : "N6Tz4XoJDMo" } ], "links" : { "self" : "/nudge/api/users/qYTkyeUkNXM/workouts" }, "size" : 1 }, "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |
Workout Detailed Data (Snapshot)
The following request is issued when an sleep graph is viewed. Each sleep data entry is added whenever a state change happens (1, 2 or 3):
The value for each item returned appears to be estimated calories burned.
Request
METHOD |
GET |
URL |
/nudge/api/workouts/2xK2B_mW6Vs/snapshot |
INPUT PARAMS |
_token=<your auth token> |
Response
{ "data" : [ [ 1322356501 , 39.0 ], [ 1322356561 , 81.0 ], [ 1322356621 , 80.0 ], [ 1322356681 , 83.0 ], [ 1322356741 , 68.0 ], [ 1322356801 , 22.0 ], [ 1322356861 , 10.0 ], [ 1322356921 , 45.0 ], [ 1322356981 , 0 ], [ 1322357041 , 0 ], [ 1322357101 , 0 ], [ 1322357161 , 0 ], [ 1322357221 , 0 ], [ 1322357281 , 0 ], [ 1322357341 , 38.0 ], [ 1322357401 , 0 ], [ 1322359141 , 0 ] ], "meta" : { "code" : 200 , "message" : "OK" , "user_xid" : "qYTkyeUXXXX" } } |