Jaa


Query pages now in Beta!

Hi guys,

Here at the OneNote API Team we continue working through our feature backlog as fast as possible to bring new APIs to you. Today we are happy to announce we just added support for querying OneNote pages in Beta! You can now call the API to query the pages a user has access to regardless of which notebook or section they are in. Yes, this includes querying for pages in both notebooks owned by and shared with the user in a single call. For instance, you can now use the API to:

  1. Build a list view of pages a user has saved to OneNote via your app and allow them to open the page in OneNote or OneNote Online.
  2. Build a "Save to OneNote" location picker that gives users full context of their notebook hierarchy (notebooks-> section groups-> sections-> pages) when choosing a save location.
  3. Query for pages containing a keyword or phrase in the title. For instance, a news app that saves articles to OneNote could use the API to allow users to search for articles that read "Seattle" in the headline.

We know there are important scenarios that require retrieving the contents of the page. We are working on this as we speak and will be announcing it pretty soon. We also know there are apps out there that just want to make it easy for users to get to the content they saved to OneNote within the context of the app and now they can do just that.

So how do you get started? The easiest way is to type your queries below or use the OneNote API Apigee console. You can quickly get an idea of the shape of the API without writing a line of code.

The OneNote API is built on the OData v4 standard which means that if you are already familiar with OData you don't have to learn a new query language to query pages in OneNote. You can expect OData verbs and functions like $filter, $orderby, and $select to just work. Here are a few page queries you can try out. They are all vanilla HTTP GET requests.

  1. [GET] https://www.onenote.com/api/beta/pages - Returns the first 20 pages a user has access to ordered by creation time desc (the default ordering).
  2. [GET] https://www.onenote.com/api/beta/pages?$skip=20&$top=100 - Returns pages 21 to 120. You can use skip and top to page through results and increase page size. The maximum page size is 100.
  3. [GET] https://www.onenote.com/api/beta/pages?$filter=contains(title, 'Seattle') - Returns the first 20 pages containing "Seattle" in the title. String comparisons are case sensitive.
  4. [GET] https://www.onenote.com/api/beta/pages?$filter=createdTime gt 2014-01-23 &$skip=20 - Returns pages 21 to 40 the user has saved to OneNote since Jan 23, 2014.
  5. [GET] https://www.onenote.com/api/beta/section/{id}/pages - Returns the first 20 pages under a specific section. You can get a list of section IDs with the following request: [GET] https://www.onenote.com/api/beta/sections.
  6. [GET] https://www.onenote.com/api/beta/pages?$select=id,title&$orderby=title asc - Returns the id and the title only the first 20 pages ordered alphabetically by title

The response is a collection of page objects in JSON format with the following structure :

 {
   "@odata.context":"https://www.onenote.com/api/Beta/$metadata#pages",
   "value":[
      {
         "createdTime":"2014-06-12T19:33:41.089Z",
         "links":{
            "oneNoteClientUrl":{
               "href":"onenote:https://d.docs.live.net/…"
            },
            "oneNoteWebUrl":{
               "href":"https://onedrive.live.com/…"
            }
         },
         "id":"{some page id}",
         "parentSection":{
            "id":"{some section id}",
            "name":"Unfiled Notes",
            "links":{
               "href":"https://www.onenote.com/api/Beta/sections/{sectionId}"
            }
         },
         "parentNotebook":{
            "id":"{some notebook id}",
            "name":"Personal (Web)",
            "links":{
               "href":"https://www.onenote.com/api/Beta/notebooks/{notebookId}"
            }
         },
         "title":"Hello World!",
         "createdByAppId":"WLID-{some app id}"
      }
   ]
}

Supported properties, verbs, operators and functions

For this beta release, we are making the following properties available which you can query using OData syntax (we will be adding more properties soon):

  • Page ID
  • Page Title
  • Page Created time
  • Page CreatedByAppId - This is the Windows Live Client ID of the app that created the page
  • Page link
  • Page OneNote Client link
  • Page OneNote Online link
  • Parent notebook ID
  • Parent notebook name
  • Parent notebook link
  • Parent section ID
  • Parent section name
  • Parent section link

And below is the list of OData operators, verbs and functions (more are coming as well):

Verbs: $filter, $orderby, $select, $top, $skip

Operator

Description

Example

Comparison Operators

 

 

eq

Equal

createdByAppId eq '{AppID}'

ne

Not equal

title ne 'Foo'

gt

Greater than

createdTime gt 2014-02-23

ge

Greater than or equal

createdTime ge 2014-05-05T07:00:00Z

lt

Less than

createdTime lt 2014-02-23

le

Less than or equal

createdTime le 2014-02-23

Logical Operators

 

 

and

Logical and

createdTime le 2014-01-30 and createdTime gt 2014-01-23

or

Logical or

createdByAppId eq 'foo' or createdByAppId eq 'bar'

not

Logical negation

not contains(title,'foo')

 

 

Function

Example

String Functions

 

contains

contains(title,'foobar')

endswith

endswith(title,'bar')

startswith

startswith(title,'foo')

length

length(title) eq 19

indexof

indexof(title,'foobar') eq 1

substring

substring(title,1) eq 'foobar'

tolower

tolower(title) eq 'foobar'

toupper

toupper(title) eq 'FOOBAR'

trim

trim(title) eq 'foobar'

concat

concat(title,'- by MyRecipesApp') eq 'Carrot Cake Recipe - by MyRecipesApp'

OAuth scopes
We are introducing 3 new OAuth scopes that you can request within your apps to be able to query pages in OneNote.

  • office.onenote - Grants read permissions to the user's notebooks and pages. Request this scope if your app needs to query pages in any of the user's notebooks.
  • office.onenote_update_by_app - Grants create, read, update, and delete permissions to pages created by the app. Request this scope if your app only needs to query pages the user created through your app.
  • office.onenote _update - Grants full access to the user's notebooks and pages. Request this scope if your app needs to query pages in any of the user's notebooks.

If you notice the actual OneNote REST API is super simple to use. Authorizing the request is perhaps the one tricky thing. We don't want you to spend any time writing boiler plate code so we have posted a few code samples (for all major platforms) on GitHub that take care of this for you so you can get right to business. You can also use the code snippets below if you want to quickly try this within an existing app.

 

Windows Store

 

Android

 

iOS

 

Windows Phone

 

JavaScript

 

  1. Get a client ID for use with the OneNote API.
  2. Get a Windows Store developer account and register you app with the Windows Store.
  3. Install and add a reference to the Live SDK within your project.
  1: using Microsoft.Live;
  2:  
  3: …
  4:  
  5: private async void QueryPages()
  6: {
  7:    var client = new LiveAuthClient();
  8:    var loginResult = await client.LoginAsync(new string[] { 
  9:              "wl.signin", 
  10:              "office.onenote" });
  11:  
  12:    if (loginResult.Status == LiveConnectSessionStatus.Connected)
  13:    {
  14:       using (var httpClient = new HttpClient())
  15:       {
  16:          HttpRequestMessage request = new HttpRequestMessage(
  17:                      HttpMethod.Get, "https://www.onenote.com/api/beta/pages");
  18:          request.Headers.Add("Authorization", "Bearer " + loginResult.Session.AccessToken);
  19:          var response = await httpClient.SendAsync(request);
  20:          var content = response.Content.ReadAsStringAsync();
  21:       }
  22:    }
  23: }
  1. Get a client ID for use with the OneNote API
  2. Download and reference the LiveSDK for Android within your project.
  1: import com.microsoft.live.LiveAuthClient;
  2: import com.microsoft.live.LiveAuthException;
  3: import com.microsoft.live.LiveAuthListener;
  4: import com.microsoft.live.LiveConnectSession;
  5: import com.microsoft.live.LiveStatus;
  6: …
  7:  
  8: public class QueryPagesSample extends Activity implements LiveAuthListener {
  9:  
  10:     private LiveAuthClient auth;
  11:     private TextView contentView;
  12:     
  13:     protected void onCreate(Bundle savedInstanceState) {
  14:         super.onCreate(savedInstanceState);
  15:  
  16:         setContentView(R.layout.activity_query_pages_sample);
  17:  
  18:         this.contentView = (TextView)findViewById(R.id.fullscreen_content);
  19:         this.auth = new LiveAuthClient(this, "YOUR CLIENT ID HERE");
  20:     }
  21:  
  22:     protected void onStart(){
  23:         super.onStart();
  24:         Iterable<String> scopes = Arrays.asList("wl.signin", "office.onenote");
  25:         this.auth.login(this,scopes, this);
  26:     }
  27:     
  28:     public void onAuthComplete(LiveStatus status, LiveConnectSession session, Object userState) {
  29:         
  30:         if(status == LiveStatus.CONNECTED) {
  31:             this.contentView.setText("Getting a list of pages...");
  32:             
  33:             String accessToken = session.getAccessToken();
  34:             
  35:             try {
  36:                 // Workaround to allow network calls to happen on the main thread for sample readability 
  37:                 // Network calls should happen on a separate task not to block the UI thread
  38:                 StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
  39:                 StrictMode.setThreadPolicy(policy); 
  40:                 
  41:                 HttpClient httpclient = new DefaultHttpClient();
  42:                 HttpGet request = new HttpGet();
  43:                 // Attaching the access token to the request headers 
  44:                 request.setHeader("Authorization", "Bearer " + accessToken);
  45:                 request.setURI(new URI("https://www.onenote.com/api/beta/pages"));
  46:                 
  47:                 HttpResponse response = httpclient.execute(request);
  48:                 BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
  49:                 
  50:                 //Read the response
  51:                 String readLine;
  52:                    StringBuffer responseString = new StringBuffer();
  53:         
  54:                       while ((readLine = br.readLine())!= null)
  55:                       {
  56:                          responseString.append(readLine);
  57:                       }
  58:                 
  59:                    this.contentView.setText(responseString.toString());
  60:                 } 
  61:                 catch (Exception e) {
  62:                       this.contentView.setText("Error.");
  63:                 }
  64:               }
  65:               else {
  66:                  this.contentView.setText("Not signed in.");
  67:               }        
  68:            }
  69:  
  70:     public void onAuthError(LiveAuthException exception, Object userState) {
  71:         this.contentView.setText("Error signing in: " + exception.getMessage());               
  72:     }
  73: }
  1. Get a client ID for use with the OneNote API
  2. Download and reference the Live SDK for iOS within your project.

ViewController.h

  1: #import <UIKit/UIKit.h>
  2: #import "LiveSDK/LiveConnectClient.h"
  3:  
  4: @interface ViewController : UIViewController<LiveAuthDelegate>
  5: @property (strong, nonatomic) LiveConnectClient *liveClient;
  6: @property (strong, nonatomic) IBOutlet UILabel *infoLabel;
  7:  
  8: @end
ViewController.m
  1: #import "ViewController.h"
  2:  
  3: @implementation ViewController
  4: @synthesize liveClient;
  5: @synthesize infoLabel;
  6:  
  7: NSString* APP_CLIENT_ID=@"YOUR APP ID HERE";
  8:  
  9: - (void)viewDidLoad
  10: {
  11:     [super viewDidLoad];
  12:     
  13:     self.liveClient = [[LiveConnectClient alloc]initWithClientId:APP_CLIENT_ID
  14:                                                         delegate:self
  15:                                                        userState:@"initialize"];
  16: }
  17:  
  18: - (void)authCompleted:(LiveConnectSessionStatus)status
  19:               session:(LiveConnectSession *)session
  20:             userState:(id)userState
  21: {
  22:     if([userState isEqual:@"initialize"])
  23:     {
  24:         [self.infoLabel setText:@"Signed in."];
  25:         [self.liveClient login:self
  26:                         scopes:[NSArray arrayWithObjects:@"wl.signin", @"office.onenote",nil]
  27:                       delegate:self
  28:                      userState:@"signin"];
  29:     }
  30:     
  31:     if ([userState isEqual:@"signin"])
  32:     {
  33:         if (session != nil)
  34:         {
  35:             [self.infoLabel setText:@"Signed in."];
  36:             
  37:             NSString*  accessToken = session.accessToken;
  38:             NSMutableURLRequest * request = [[NSMutableURLRequest alloc]init];
  39:             [request setHTTPMethod:@"GET"];
  40:             [request setURL:[NSURL URLWithString: @"https://www.onenote.com/api/beta/pages"]];
  41:             [request setValue:[@"Bearer " stringByAppendingString:accessToken] forHTTPHeaderField:@"Authorization"];
  42:  
  43:             NSHTTPURLResponse* urlResponse = nil;
  44:             NSError *error = [[NSError alloc] init];
  45:             
  46:             NSData *responseData = [NSURLConnection sendSynchronousRequest:request
  47:                                                          returningResponse:&urlResponse
  48:                                                                      error:&error];
  49:             NSString *result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
  50:             [self.infoLabel setText: result];
  51:         }
  52:     }
  53: }
  54:  
  55: - (void)authFailed:(NSError *) error
  56:          userState:(id)userState
  57: {
  58:     [self.infoLabel setText:[NSString stringWithFormat:@"Error: %@", [error localizedDescription]]];
  59: }
  60:  
  61: - (void)didReceiveMemoryWarning
  62: {
  63:     [super didReceiveMemoryWarning];
  64:     // Dispose of any resources that can be recreated.
  65: }
  66: @end
  1. Get a client ID for use with the OneNote API
  2. Get a Windows Store developer account and register you app with the Windows Phone Store.
  3. Install and add a reference to the Live SDK for WP within your project.
  1: using Microsoft.Live;     
  2:  
  3: …
  4:  
  5: private async void QueryPages()
  6: {
  7:    var authClient = new LiveAuthClient();
  8:    LiveLoginResult result = await authClient.LoginAsync(new string[] { "wl.signin", "office.onenote" });
  9:  
  10:    if (result.Status == LiveConnectSessionStatus.Connected)
  11:    {
  12:       using (var httpClient = new HttpClient())
  13:       {
  14:          HttpRequestMessage request = new HttpRequestMessage(
  15:             HttpMethod.Get, "https://www.onenote.com/api/beta/pages");
  16:  
  17:          request.Headers.Add("Authorization", "Bearer " + result.Session.AccessToken);
  18:  
  19:          var response = await httpClient.SendAsync(request);
  20:          var content = await response.Content.ReadAsStringAsync();
  21:          this.responseTextBlock.Text = content;
  22:       }
  23:    }
  24: }
  1. Get a client ID for use with the OneNote API
  2. Set up a redirect URI for your app
  1: <html>
  2: <head>
  3:     <title>OneNote Query Pages JavaScript Sample</title>
  4:     <script src="//js.live.net/v5.0/wl.js"></script>
  5:     <script src="//code.jquery.com/jquery-1.9.1.js"></script>
  6: </head>
  7: <body>
  8:     <div id="signin"></div>
  9:     <p id="response"></p>
  10:     <script>
  11:         var access_token;
  12:  
  13:         WL.Event.subscribe("auth.login", onLogin);
  14:         WL.init({
  15:             client_id: 'YOUR CLIENT ID GOES HERE',
  16:             redirect_uri: 'YOUR APP REDIRECT GOES HERE',
  17:             scope: ["wl.signin","office.onenote"], //specify read access scope
  18:             response_type: "token"
  19:         });
  20:  
  21:         WL.ui({
  22:             name: "signin",
  23:             element: "signin"
  24:         });
  25:  
  26:         function onLogin(session) {
  27:             if (!session.error) {
  28:  
  29:                 access_token = getAccessToken();
  30:  
  31:                 document.getElementById("response").innerText = "Getting a list of pages...";
  32:  
  33:                 $.ajax({
  34:                     accept: "application/json",
  35:                     type: "GET",
  36:                     url: "https://www.onenote.com/api/beta/pages",
  37:                     headers: { "Authorization": "Bearer " + access_token },
  41:                     complete: function (data, status, xhr) {
  42:                         document.getElementById("response").innerText = data.responseText;
  43:                     }
  47:                 });
  48:             }
  49:         }
  50:  
  51:         //Extracts the access token
  52:         function getAccessToken() {
  53:  
  54:             var access_token_string = "access_token", access_token = null;
  55:             var cookie_value = document.cookie;
  56:             var token_start = cookie_value.indexOf(access_token_string + "=");
  57:  
  58:             if (token_start == -1)
  59:             {
  60:                 access_token = null;
  61:             }
  62:             else
  63:             {
  64:                 token_start += 13;
  65:  
  66:                 var token_end = cookie_value.indexOf("&", token_start);
  67:  
  68:                 if (token_end == -1)
  69:                 {
  70:                     token_end = cookie_value.length;
  71:                 }
  72:  
  73:                 access_token = unescape(cookie_value.substring(token_start, token_end));
  74:             }
  75:  
  76:             return access_token;
  77:         }
  78:     </script>
  79: </body>
  80: </html>

Help us make it better
As with the rest of our API features in Beta, we want you to try page queries out in your apps and give us feedback. Tell us what tweaks you think we should make to simplify your code, what things are missing to complete your scenario, what are the things you like that we should leave untouched, and if you think we just got it plain wrong we want to hear that too :)! Help us make the API better for you and the rest of the OneNote developer community so once it sticks it sticks for good. We are planning to leave this feature in Beta for at least one month (possibly more) for you to try it out and post your suggestions. We will iterate on your feedback and release to production soon after.

Please post your recommendations as comments to this post. for feature requests you can add to the list or vote on UserVoice. Finally, if you have any problems with the OneNote API in general please post your questions to stackoverflow.

Look out for more updates coming soon and happy coding.

- Omar

Comments

  • Anonymous
    June 19, 2014
    This is awesome!

  • Anonymous
    July 31, 2014
    It seems like parentNotebook, parentSection and parentSectionGroup concepts have disappeared. I guess such changes are expected in beta but wanted to make sure if they are gone for good or they will come back at some point. It would be quite useful to know as I am building an SDK and the change breaks my integration tests.

  • Anonymous
    August 01, 2014
    Hi Cem, Yes. Parent navigation properties are coming back soon. We thought it would be a better idea to make them available as part of supporting the $expand verb across the API. Expand will allow apps to optimize their API calling patterns by requesting properties from multiple entity types in a single RT. Stay tuned.

  • Anonymous
    August 06, 2014
    Is it possible to get page's content in html format ?

  • Anonymous
    September 24, 2014
    @mpiu: Yes, it is possible to get the page's content in html format. Check out the newest Beta feature: blogs.msdn.com/.../new-beta-api-page-recall.aspx

  • Anonymous
    October 18, 2014
    Is there an updatedTime that could be added to the API or does createdTime incorporate creates and updates?

  • Anonymous
    October 20, 2014
    Hi Troy, We plan to add more page properties including the last modified time of the page. Any other properties you would like to see?

  • Anonymous
    December 30, 2014
    3.[GET] www.onenote.com/.../pages$filter=contains(title, 'Seattle') - Returns the first 20 pages containing "seattle" or "Seattle" in the title. String comparisons are case insensitive This seems wrong. I am NOT seeing this call being case-sensitive.

  • Anonymous
    January 26, 2015
    Hi, Thanks for the feedback. The current behavior is case-sensitive for string comparisons. I have updated the post to reflect this.

  • Anonymous
    February 12, 2015
    How would I be able to access this information, say, using an oData data connection in Excel?

  • Anonymous
    February 12, 2015
    Could really use some examples in VISUAL BASIC and VBA. Not everyone codes in C#!

  • Anonymous
    February 12, 2015
    The comment has been removed

  • Anonymous
    August 17, 2015
    parentSection used to work until a few days ago, but very recently (days ago?) the API stopped returning it again. Any idea what that might be?