The data browser is built for Chrome, Firefox, Safari, and IE9 and above. Please upgrade your browser, or download Google Chrome to get the best experience.

REST API

The REST API lets you interact with Parse from anything that can send an HTTP request. There are many things you can do with the REST API. For example:

  • A mobile website can access Parse data from Javascript.
  • A webserver can show data from Parse on a website.
  • You can upload large amounts of data that will later be consumed in a mobile app.
  • You can download recent data to run your own custom analytics.
  • Applications written in any programming language can interact with data on Parse.
  • You can export all of your data if you no longer want to use Parse.

Quick Reference

All API access is over HTTPS, and accessed via the https://api.parse.com domain. The relative path prefix /1/ indicates that we are currently using version 1 of the API.

Objects

URLHTTP VerbFunctionality
/1/classes/<className> POST Creating Objects
/1/classes/<className>/<objectId> GET Retrieving Objects
/1/classes/<className>/<objectId> PUT Updating Objects
/1/classes/<className> GET Queries
/1/classes/<className>/<objectId> DELETE Deleting Objects

Users

URLHTTP VerbFunctionality
/1/users POST Signing Up
Linking Users
/1/login GET Logging In
/1/users/<objectId> GET Retrieving Users
/1/users/me GET Validating Session Tokens
Retrieving Current User
/1/users/<objectId> PUT Updating Users
Linking Users
Verifying Emails
/1/users GET Querying Users
/1/users/<objectId> DELETE Deleting Users
/1/requestPasswordReset POST Requesting A Password Reset

Roles

URLHTTP VerbFunctionality
/1/roles POST Creating Roles
/1/roles/<objectId> GET Retrieving Roles
/1/roles/<objectId> PUT Updating Roles
/1/roles GET Querying Roles
/1/roles/<objectId> DELETE Deleting Roles

Files

URLHTTP VerbFunctionality
/1/files/<fileName> POST Uploading Files

Analytics

URLHTTP VerbFunctionality
/1/events/AppOpened POST Analytics
/1/events/<eventName> POST Custom Analytics

Push Notifications

URLHTTP VerbFunctionality
/1/push POST Push Notifications

Installations

URLHTTP VerbFunctionality
/1/installations POST Uploading Installation Data
/1/installations/<objectId> GET Retrieving Installations
/1/installations/<objectId> PUT Updating Installations
/1/installations GET Querying Installations
/1/installations/<objectId> DELETE Deleting Installations

Cloud Functions

URLHTTP VerbFunctionality
/1/functions POST Calling Cloud Functions
/1/jobs POST Triggering Background Jobs

Request Format

For POST and PUT requests, the request body must be JSON, with the Content-Type header set to application/json.

Authentication is done via HTTP headers. The X-Parse-Application-Id header identifies which application you are accessing, and the X-Parse-REST-API-Key header authenticates the endpoint. In the examples that follow, the keys for your app are included in the command. You can use the drop-down to construct example code for other apps.

You may also authenticate your REST API requests using basic HTTP authentication. For example, to retrieve an object you could set the URL using your Parse credentials in the following format:

https://myAppID:javascript-key=myJavaScriptKey@api.parse.com/1/classes/GameScore/Ed1nuqPvcm

For Javascript usage, the Parse Cloud supports cross-origin resource sharing, so that you can use these headers in conjunction with XMLHttpRequest.

Response Format

The response format for all requests is a JSON object.

Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. When a request fails, the response body is still JSON, but always contains the fields code and error which you can inspect to use for debugging. For example, trying to save an object with invalid keys will return the message:

{
  "code": 105,
  "error": "invalid field name: bl!ng"
}

Objects

Object Format

Storing data through the Parse REST API is built around a JSON encoding of the object's data. This data is schemaless, which means that you don't need to specify ahead of time what keys exist on each object. You simply set whatever key-value pairs you want, and the backend will store it.

For example, let's say you're tracking high scores for a game. A single object could contain:

{
  "score": 1337,
  "playerName": "Sean Plott",
  "cheatMode": false
}

Keys must be alphanumeric strings. Values can be anything that can be JSON-encoded.

Each object has a class name that you can use to distinguish different sorts of data. For example, we could call the high score object a GameScore. We recommend that you NameYourClassesLikeThis and nameYourKeysLikeThis, just to keep your code looking pretty.

When you retrieve objects from Parse, some fields are automatically added: createdAt, updatedAt, and objectId. These field names are reserved, so you cannot set them yourself. The object above could look like this when retrieved:

{
  "score": 1337,
  "playerName": "Sean Plott",
  "cheatMode": false,
  "createdAt": "2011-08-20T02:06:57.931Z",
  "updatedAt": "2011-08-20T02:06:57.931Z",
  "objectId": "Ed1nuqPvcm"
}

createdAt and updatedAt are UTC timestamps stored in ISO 8601 format with millisecond precision: YYYY-MM-DDTHH:MM:SS.MMMZ. objectId is a string unique to this class that identifies this object.

In the REST API, the class-level operations operate on a resource based on just the class name. For example, if the class name is GameScore, the class URL is:

https://api.parse.com/1/classes/GameScore

Users have a special class-level url:

https://api.parse.com/1/users

The operations specific to a single object are available a nested URL. For example, operations specific to the GameScore above with objectId equal to Ed1nuqPvcm would use the object URL:

https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Creating Objects

To create a new object on Parse, send a POST request to the class URL containing the contents of the object. For example, to create the object described above:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/classes/GameScore', json.dumps({
       "score": 1337,
       "playerName": "Sean Plott",
       "cheatMode": False
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \
  https://api.parse.com/1/classes/GameScore

When the creation is successful, the HTTP response is a 201 Created and the Location header contains the object URL for the new object:

Status: 201 Created
Location: https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

The response body is a JSON object containing the objectId and the createdAt timestamp of the newly-created object:

{
  "createdAt": "2011-08-20T02:06:57.931Z",
  "objectId": "Ed1nuqPvcm"
}

Retrieving Objects

Once you've created an object, you can retrieve its contents by sending a GET request to the object URL returned in the location header. For example, to retrieve the object we created above:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/classes/GameScore/Ed1nuqPvcm', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

The response body is a JSON object containing all the user-provided fields, plus the createdAt, updatedAt, and objectId fields:

{
  "score": 1337,
  "playerName": "Sean Plott",
  "cheatMode": false,
  "skills": [
    "pwnage",
    "flying"
  ],
  "createdAt": "2011-08-20T02:06:57.931Z",
  "updatedAt": "2011-08-20T02:06:57.931Z",
  "objectId": "Ed1nuqPvcm"
}

When retrieving objects that have pointers to children, you can fetch child objects by using the include option. For instance, to fetch the object pointed to by the "game" key:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"include":"game"})
connection.connect()
connection.request('GET', '/1/classes/GameScore/Ed1nuqPvcm?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'include=game' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Updating Objects

To change the data on an object that already exists, send a PUT request to the object URL. Any keys you don't specify will remain unchanged, so you can update just a subset of the object's data. For example, if we wanted to change the score field of our object:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "score": 73453
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"score":73453}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

The response body is a JSON object containing just an updatedAt field with the timestamp of the update.

{
  "updatedAt": "2011-08-21T18:02:52.248Z"
}

Counters

To help with storing counter-type data, Parse provides the ability to atomically increment (or decrement) any number field. So, we can increment the score field like so:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "score": {
         "__op": "Increment",
         "amount": 1
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"score":{"__op":"Increment","amount":1}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

To decrement the counter, use the Increment operator with a negative number:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "score": {
         "__op": "Increment",
         "amount": -1
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"score":{"__op":"Increment","amount":-1}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Arrays

To help with storing array data, there are three operations that can be used to atomically change an array field:

  • Add appends the given array of objects to the end of an array field.
  • AddUnique adds only the given objects which aren't already contained in an array field to that field. The position of the insert is not guaranteed.
  • Remove removes all instances of each given object from an array field.

Each method takes an array of objects to add or remove in the "objects" key. For example, we can add items to the set-like "skills" field like so:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "skills": {
         "__op": "AddUnique",
         "objects": [
           "flying",
           "kungfu"
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"skills":{"__op":"AddUnique","objects":["flying","kungfu"]}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Relations

In order to update Relation types, Parse provides special operators to atomically add and remove objects to a relation. So, we can add an object to a relation like so:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "opponents": {
         "__op": "AddRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "Player",
             "objectId": "Vx4nudeWn"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"opponents":{"__op":"AddRelation","objects":[{"__type":"Pointer","className":"Player","objectId":"Vx4nudeWn"}]}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

To remove an object from a relation, you can do:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "opponents": {
         "__op": "RemoveRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "Player",
             "objectId": "Vx4nudeWn"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"opponents":{"__op":"RemoveRelation","objects":[{"__type":"Pointer","className":"Player","objectId":"Vx4nudeWn"}]}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Deleting Objects

To delete an object from the Parse Cloud, send a DELETE request to its object URL. For example:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/classes/GameScore/Ed1nuqPvcm', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

You can delete a single field from an object by using the Delete operation:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/classes/GameScore/Ed1nuqPvcm', json.dumps({
       "opponents": {
         "__op": "Delete"
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"opponents":{"__op":"Delete"}}' \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Batch Operations

To reduce the amount of time spent on network round trips, you can create, update, or delete up to 50 objects in one call, using the batch endpoint.

Each command in a batch has method, path, and body parameters that specify the HTTP command that would normally be used for that command. The commands are run in the order they are given. For example, to create a couple of GameScore objects:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/batch', json.dumps({
       "requests": [
         {
           "method": "POST",
           "path": "/1/classes/GameScore",
           "body": {
             "score": 1337,
             "playerName": "Sean Plott"
           }
         },
         {
           "method": "POST",
           "path": "/1/classes/GameScore",
           "body": {
             "score": 1338,
             "playerName": "ZeroCool"
           }
         }
       ]
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "requests": [
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1337,
              "playerName": "Sean Plott"
            }
          },
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1338,
              "playerName": "ZeroCool"
            }
          }
        ]
      }' \
  https://api.parse.com/1/batch

The response from batch will be a list with the same number of elements as the input list. Each item in the list with be a dictionary with either the success or error field set. The value of success will be the normal response to the equivalent REST command:

{
  "success": {
    "createdAt": "2012-06-15T16:59:11.276Z",
    "objectId": "YAfSAWwXbL"
  }
}

The value of error will be an object with a numeric code and error string:

{
  "error": {
    "code": 101,
    "error": "object not found for delete"
  }
}

Other commands that work in a batch are update and delete.

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/batch', json.dumps({
       "requests": [
         {
           "method": "PUT",
           "path": "/1/classes/GameScore/Ed1nuqPvcm",
           "body": {
             "score": 999999
           }
         },
         {
           "method": "DELETE",
           "path": "/1/classes/GameScore/Cpl9lrueY5"
         }
       ]
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "requests": [
          {
            "method": "PUT",
            "path": "/1/classes/GameScore/Ed1nuqPvcm",
            "body": {
              "score": 999999
            }
          },
          {
            "method": "DELETE",
            "path": "/1/classes/GameScore/Cpl9lrueY5"
          }
        ]
      }' \
  https://api.parse.com/1/batch

Data Types

So far we have only used values that can be encoded with standard JSON. The Parse mobile client libraries also support dates, binary data, and relational data. In the REST API, these values are encoded as JSON hashes with the __type field set to indicate their type, so you can read or write these fields if you use the correct encoding.

The Date type contains a field iso which contains a UTC timestamp stored in ISO 8601 format with millisecond precision: YYYY-MM-DDTHH:MM:SS.MMMZ.

{
  "__type": "Date",
  "iso": "2011-08-21T18:02:52.249Z"
}

Dates are useful in combination with the built-in createdAt and updatedAt fields. For example, to retrieve objects created since a particular time, just encode a Date in a comparison query:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "createdAt": {
         "$gte": {
           "__type": "Date",
           "iso": "2011-08-21T18:02:52.249Z"
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"createdAt":{"$gte":{"__type":"Date","iso":"2011-08-21T18:02:52.249Z"}}}' \
  https://api.parse.com/1/classes/GameScore

The Bytes type contains a field base64 which contains a base64 encoding of binary data. The specific base64 encoding is the one used by MIME, and does not contain whitespace.

{
  "__type": "Bytes",
  "base64": "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw=="
}

The Pointer type is used when mobile code sets a PFObject or ParseObject as the value of another object. It contains the className and objectId of the referred-to value.

{
  "__type": "Pointer",
  "className": "GameScore",
  "objectId": "Ed1nuqPvc"
}

Pointers to user objects have a className of _User. Prefixing with an underscore is forbidden for developer-defined classes and signifies the class is a special built-in.

The Relation type is used for many-to-many relations when the mobile uses PFRelation or ParseRelation as a value. It has a className that is the class name of the target objects.

{
  "__type": "Relation",
  "className": "GameScore"
}

When querying, Relation objects behave like arrays of Pointers. Any operation that is valid for arrays of pointers (other than include) works for Relation objects.

When more data types are added, they will also be represented as hashes with a __type field set, so you may not use this field yourself on JSON objects.

Queries

Basic Queries

You can retrieve multiple objects at once by sending a GET request to the class URL. Without any URL parameters, this simply lists objects in the class:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/classes/GameScore', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore

The return value is a JSON object that contains a results field with a JSON array that lists the objects.

{
  "results": [
    {
      "playerName": "Jang Min Chul",
      "updatedAt": "2011-08-19T02:24:17.787Z",
      "cheatMode": false,
      "createdAt": "2011-08-19T02:24:17.787Z",
      "objectId": "A22v5zRAgd",
      "score": 80075
    },
    {
      "playerName": "Sean Plott",
      "updatedAt": "2011-08-21T18:02:52.248Z",
      "cheatMode": false,
      "createdAt": "2011-08-20T02:06:57.931Z",
      "objectId": "Ed1nuqPvcm",
      "score": 73453
    }
  ]
}

Query Constraints

There are several ways to put constraints on the objects found, using the where URL parameter. The value of the where parameter should be encoded JSON. Thus, if you look at the actual URL requested, it would be JSON-encoded, then URL-encoded. The simplest use of the where parameter is constraining the value for keys. For example, if we wanted to retrieve Sean Plott's scores that were not in cheat mode, we could do:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "playerName": "Sean Plott",
       "cheatMode": False
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"playerName":"Sean Plott","cheatMode":false}' \
  https://api.parse.com/1/classes/GameScore

The values of the where parameter also support comparisons besides exact matching. Instead of an exact value, provide a hash with keys corresponding to the comparisons to do. The where parameter supports these options:

Key Operation
$lt Less Than
$lte Less Than Or Equal To
$gt Greater Than
$gte Greater Than Or Equal To
$ne Not Equal To
$in Contained In
$nin Not Contained in
$exists A value is set for the key
$select This matches a value for a key in the result of a different query
$dontSelect Requires that a key's value not match a value for a key in the result of a different query
$all Contains all of the given values

For example, to retrieve scores between 1000 and 3000, including the endpoints, we could issue:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "score": {
         "$gte": 1000,
         "$lte": 3000
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"score":{"$gte":1000,"$lte":3000}}' \
  https://api.parse.com/1/classes/GameScore

To retrieve scores equal to an odd number below 10, we could issue:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "score": {
         "$in": [
           1,
           3,
           5,
           7,
           9
         ]
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"score":{"$in":[1,3,5,7,9]}}' \
  https://api.parse.com/1/classes/GameScore

To retrieve scores not by a given list of players we could issue:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "playerName": {
         "$nin": [
           "Jonathan Walsh",
           "Dario Wunsch",
           "Shawn Simon"
         ]
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"playerName":{"$nin":["Jonathan Walsh","Dario Wunsch","Shawn Simon"]}}' \
  https://api.parse.com/1/classes/GameScore

To retrieve documents with the score set, we could issue:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "score": {
         "$exists": True
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"score":{"$exists":true}}' \
  https://api.parse.com/1/classes/GameScore

To retrieve documents without the score set, we could issue:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "score": {
         "$exists": False
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"score":{"$exists":false}}' \
  https://api.parse.com/1/classes/GameScore

If you have a class containing sports teams and you store a user's hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "hometown": {
         "$select": {
           "query": {
             "className": "Team",
             "where": {
               "winPct": {
                 "$gt": 0.5
               }
             }
           },
           "key": "city"
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/_User?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"hometown":{"$select":{"query":{"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}}' \
  https://api.parse.com/1/classes/_User

You can use the order parameter to specify a field to sort by. Prefixing with a negative sign reverses the order. Thus, to retrieve scores in ascending order:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"order":"score"})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'order=score' \
  https://api.parse.com/1/classes/GameScore

And to retrieve scores in descending order:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"order":"-score"})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'order=-score' \
  https://api.parse.com/1/classes/GameScore

You can sort by multiple fields by passing order a comma-separated list. To retrieve documents that are ordered by scores in ascending order and the names in descending order:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"order":"score,-name"})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'order=score,-name' \
  https://api.parse.com/1/classes/GameScore

You can use the limit and skip parameters for pagination. limit defaults to 100, but anything from 1 to 1000 is a valid limit. Thus, to retrieve 200 objects after skipping the first 400:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"limit":200,"skip":400})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'limit=200' \
  --data-urlencode 'skip=400' \
  https://api.parse.com/1/classes/GameScore

You can restrict the fields returned by passing keys a comma-separated list. To retrieve documents that contain only the score and playerName fields (and also special built-in fields such as objectId, createdAt, and updatedAt):

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"keys":"score,playerName"})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'keys=score,playerName' \
  https://api.parse.com/1/classes/GameScore

All of these parameters can be used in combination with each other.

Queries on Array Values

For keys with an array type, you can find objects where the key's array value contains 2 by:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "arrayKey": 2
     })})
connection.connect()
connection.request('GET', '/1/classes/RandomObject?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"arrayKey":2}' \
  https://api.parse.com/1/classes/RandomObject

You can also use the $all operator to find objects with an array field which contains each of the values 2, 3, and 4 by:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "arrayKey": {
         "$all": [
           2,
           3,
           4
         ]
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/RandomObject?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"arrayKey":{"$all":[2,3,4]}}' \
  https://api.parse.com/1/classes/RandomObject

Relational Queries

There are several ways to issue queries for relational data. If you want to retrieve objects where a field matches a particular object, you can use a where clause with a Pointer encoded with __type just like you would use other data types. For example, if each Comment has a Post object in its post field, you can fetch comments for a particular Post:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "post": {
         "__type": "Pointer",
         "className": "Post",
         "objectId": "8TOXdXf3tz"
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/Comment?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"post":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"}}' \
  https://api.parse.com/1/classes/Comment

If you want to retrieve objects where a field contains an object that matches another query, you can use the $inQuery operator. Note that the default limit of 100 and maximum limit of 1000 apply to the inner query as well, so with large data sets you may need to construct queries carefully to get the desired behavior. For example, imagine you have Post class and a Comment class, where each Comment has a relation to its parent Post. You can find comments on posts with images by doing:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "post": {
         "$inQuery": {
           "where": {
             "image": {
               "$exists": True
             }
           },
           "className": "Post"
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/Comment?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"post":{"$inQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}' \
  https://api.parse.com/1/classes/Comment

If you want to retrieve objects where a field contains an object that does not match another query, you can use the $notInQuery operator. Imagine you have Post class and a Comment class, where each Comment has a relation to its parent Post. You can find comments on posts without images by doing:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "post": {
         "$notInQuery": {
           "where": {
             "image": {
               "$exists": True
             }
           },
           "className": "Post"
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/Comment?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"post":{"$notInQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}' \
  https://api.parse.com/1/classes/Comment

If you want to retrieve objects that are members of Relation field of a parent object, you can use the $relatedTo operator. Imagine you have a Post class and User class, where each Post can be liked by many users. If the Users that liked a Post was stored in a Relation on the post under the key likes, you, can the find the users that liked a particular post by:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "$relatedTo": {
         "object": {
           "__type": "Pointer",
           "className": "Post",
           "objectId": "8TOXdXf3tz"
         },
         "key": "likes"
       }
     })})
connection.connect()
connection.request('GET', '/1/users?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"$relatedTo":{"object":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"},"key":"likes"}}' \
  https://api.parse.com/1/users

In some situations, you want to return multiple types of related objects in one query. You can do this by passing the field to include in the include parameter. For example, let's say you are retrieving the last ten comments, and you want to retrieve their related posts at the same time:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"order":"-createdAt","limit":10,"include":"post"})
connection.connect()
connection.request('GET', '/1/classes/Comment?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'order=-createdAt' \
  --data-urlencode 'limit=10' \
  --data-urlencode 'include=post' \
  https://api.parse.com/1/classes/Comment

Instead of being represented as a Pointer, the post field is now expanded into the whole object. __type is set to Object and className is provided as well. For example, a Pointer to a Post could be represented as:

{
  "__type": "Pointer",
  "className": "Post",
  "objectId": "8TOXdXf3tz"
}

When the query is issued with an include parameter for the key holding this pointer, the pointer will be expanded to:

{
  "__type": "Object",
  "className": "Post",
  "objectId": "8TOXdXf3tz",
  "createdAt": "2011-12-06T20:59:34.428Z",
  "updatedAt": "2011-12-06T20:59:34.428Z",
  "otherFields": "willAlsoBeIncluded"
}

You can also do multi level includes using dot notation. If you wanted to include the post for a comment and the post's author as well you can do:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"order":"-createdAt","limit":10,"include":"post.author"})
connection.connect()
connection.request('GET', '/1/classes/Comment?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'order=-createdAt' \
  --data-urlencode 'limit=10' \
  --data-urlencode 'include=post.author' \
  https://api.parse.com/1/classes/Comment

You can issue a query with multiple fields included by passing a comma-separated list of keys as the include parameter.

Counting Objects

If you are limiting your query, or if there are a very large number of results, and you want to know how many total results there are without returning them all, you can use the count parameter. For example, if you only care about the number of games played by a particular player:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "playerName": "Jonathan Walsh"
     }),"count":1,"limit":0})
connection.connect()
connection.request('GET', '/1/classes/GameScore?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"playerName":"Jonathan Walsh"}' \
  --data-urlencode 'count=1' \
  --data-urlencode 'limit=0' \
  https://api.parse.com/1/classes/GameScore

Since this requests a count as well as limiting to zero results, there will be a count but no results in the response.

{
  "results": [

  ],
  "count": 1337
}

With a nonzero limit, that request would return results as well as the count.

Compound Queries

If you want to find objects that match one of several queries, you can use $or operator, with a JSONArray as its value. For instance, if you want to find players with either have a lot of wins or a few wins, you can do:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "$or": [
         {
           "wins": {
             "$gt": 150
           }
         },
         {
           "wins": {
             "$lt": 5
           }
         }
       ]
     })})
connection.connect()
connection.request('GET', '/1/classes/Player?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={"$or":[{"wins":{"$gt":150}},{"wins":{"$lt":5}}]}' \
  https://api.parse.com/1/classes/Player

Any other constraints on the query are also applied to the object returned, so you can add other constraints to queries with $or.

Note that we do not, however, support non-filtering constraints (e.g. limit, skip, sort, include) in the subqueries of the compound query.

Users

Many apps have a unified login that works across the mobile app and other systems. Accessing user accounts through the REST API lets you build this functionality on top of Parse.

In general, users have the same features as other objects, such as the flexible schema. The differences are that user objects must have a username and password, the password is automatically encrypted and stored securely, and Parse enforces the uniqueness of the username and email fields.

Signing Up

Signing up a new user differs from creating a generic object in that the username and password fields are required. The password field is handled differently than the others; it is encrypted when stored in the Parse Cloud and never returned to any client request.

You can ask Parse to verify user email addresses in your application settings page. With this setting enabled, all new user registrations with an email field will generate an email confirmation at that address. You can check whether the user has verified their email with the emailVerified field.

To sign up a new user, send a POST request to the users root. You may add any additional fields. For example, to create a user with a specific phone number:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/users', json.dumps({
       "username": "cooldude6",
       "password": "p_n7!-e8",
       "phone": "415-392-0202"
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"username":"cooldude6","password":"p_n7!-e8","phone":"415-392-0202"}' \
  https://api.parse.com/1/users

When the creation is successful, the HTTP response is a 201 Created and the Location header contains the URL for the new user:

Status: 201 Created
Location: https://api.parse.com/1/users/g7y9tkhB7O

The response body is a JSON object containing the objectId, the createdAt timestamp of the newly-created object, and the sessionToken which can be used to authenticate subsequent requests as this user:

{
  "createdAt": "2011-11-07T20:58:34.448Z",
  "objectId": "g7y9tkhB7O",
  "sessionToken": "pnktnjyb996sj4p156gjtp4im"
}

Logging In

After you allow users to sign up, you need to let them log in to their account with a username and password in the future. To do this, send a GET request to the /1/login endpoint with username and password as URL-encoded parameters:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"username":"cooldude6","password":"p_n7!-e8"})
connection.connect()
connection.request('GET', '/1/login?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'username=cooldude6' \
  --data-urlencode 'password=p_n7!-e8' \
  https://api.parse.com/1/login

The response body is a JSON object containing all the user-provided fields except password. It also contains the createdAt, updatedAt, objectId, and sessionToken fields:

{
  "username": "cooldude6",
  "phone": "415-392-0202",
  "createdAt": "2011-11-07T20:58:34.448Z",
  "updatedAt": "2011-11-07T20:58:34.448Z",
  "objectId": "g7y9tkhB7O",
  "sessionToken": "pnktnjyb996sj4p156gjtp4im"
}

Verifying Emails

Enabling email verification in an application's settings allows the application to reserve part of its experience for users with confirmed email addresses. Email verification adds the emailVerified field to the User object. When a User's email is set or modified, emailVerified is set to false. Parse then emails the user a link which will set emailVerified to true.

There are three emailVerified states to consider:

  1. true - the user confirmed his or her email address by clicking on the link Parse emailed them. Users can never have a true value when the user account is first created.
  2. false - at the time the User object was last refreshed, the user had not confirmed his or her email address. If emailVerified is false, consider refreshing the User object.
  3. missing - the User was created when email verification was off or the User does not have an email.

Requesting A Password Reset

You can initiate password resets for users who have emails associated with their account. To do this, send a POST request to /1/requestPasswordReset endpoint with email in the body of the request:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/requestPasswordReset', json.dumps({
       "email": "coolguy@iloveapps.com"
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"email":"coolguy@iloveapps.com"}' \
  https://api.parse.com/1/requestPasswordReset

If successful, the response body is an empty JSON object.

Retrieving Users

You can also retrieve the contents of a user object by sending a GET request to the URL returned in the location header when it was created. For example, to retrieve the user created above:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/users/g7y9tkhB7O', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/users/g7y9tkhB7O

The response body is a JSON object containing all the user-provided fields except password. It also contains the createdAt, updatedAt, and objectId fields:

{
  "username": "cooldude6",
  "phone": "415-392-0202",
  "createdAt": "2011-11-07T20:58:34.448Z",
  "updatedAt": "2011-11-07T20:58:34.448Z",
  "objectId": "g7y9tkhB7O"
}

Validating Session Tokens / Retrieving Current User

With a valid session token, you can send a GET request to the /1/users/me endpoint to retrieve the user associated with that session token:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/users/me', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "pnktnjyb996sj4p156gjtp4im"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: pnktnjyb996sj4p156gjtp4im" \
  https://api.parse.com/1/users/me

The response matches the JSON object above for retrieving users. If the session token is not valid, an error object is returned:

{
  "code": 101,
  "error": "invalid session"
}

Updating Users

In normal usage, nobody except the user is allowed to modify their own data. To authenticate themselves, the user must add a X-Parse-Session-Token header to the request with the session token provided by the signup or login method.

To change the data on a user that already exists, send a PUT request to the user URL. Any keys you don't specify will remain unchanged, so you can update just a subset of the user's data. username and password may be changed, but the new username must not already be in use.

For example, if we wanted to change the phone number for cooldude6:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/users/g7y9tkhB7O', json.dumps({
       "phone": "415-369-6201"
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "pnktnjyb996sj4p156gjtp4im",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: pnktnjyb996sj4p156gjtp4im" \
  -H "Content-Type: application/json" \
  -d '{"phone":"415-369-6201"}' \
  https://api.parse.com/1/users/g7y9tkhB7O

The response body is a JSON object containing just an updatedAt field with the timestamp of the update.

{
  "updatedAt": "2011-11-07T21:25:10.623Z"
}

Querying

You can retrieve multiple users at once by sending a GET request to the root users URL. Without any URL parameters, this simply lists users:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/users', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/users

The return value is a JSON object that contains a results field with a JSON array that lists the objects.

{
  "results": [
    {
      "username": "bigglesworth",
      "phone": "650-253-0000",
      "createdAt": "2011-11-07T20:58:06.445Z",
      "updatedAt": "2011-11-07T20:58:06.445Z",
      "objectId": "3KmCvT7Zsb"
    },
    {
      "username": "cooldude6",
      "phone": "415-369-6201",
      "createdAt": "2011-11-07T20:58:34.448Z",
      "updatedAt": "2011-11-07T21:25:10.623Z",
      "objectId": "g7y9tkhB7O"
    }
  ]
}

All of the options for queries that work for regular objects also work for user objects, so check the section on Querying Objects for more details.

Deleting Users

To delete a user from the Parse Cloud, send a DELETE request to its URL. You must provide the X-Parse-Session-Token header to authenticate. For example:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/users/g7y9tkhB7O', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "pnktnjyb996sj4p156gjtp4im"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: pnktnjyb996sj4p156gjtp4im" \
  https://api.parse.com/1/users/g7y9tkhB7O

Linking Users

Parse allows you to link your users with services like Twitter and Facebook, enabling your users to sign up or log into your application using their existing identities. This is accomplished through the sign-up and update REST endpoints by providing authentication data for the service you wish to link to a user in the authData field. Once your user is associated with a service, the authData for the service will be stored with the user and is retrievable by logging in.

authData is a JSON object with keys for each linked service containing the data below. In each case, you are responsible for completing the authentication flow (e.g. OAuth 1.0a) to obtain the information the the service requires for linking.

Facebook authData contents:

{
  "facebook": {
    "id": "user's Facebook id number as a string",
    "access_token": "an authorized Facebook access token for the user",
    "expiration_date": "token expiration date of the format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
  }
}

Twitter authData contents:

{
  "twitter": {
    "id": "user's Twitter id number as a string",
    "screen_name": "user's Twitter screen name",
    "consumer_key": "your application's consumer key",
    "consumer_secret": "your application's consumer secret",
    "auth_token": "an authorized Twitter token for the user with your application",
    "auth_token_secret": "the secret associated with the auth_token"
  }
}

Anonymous user authData contents:

{
  "anonymous": {
    "id": "random UUID with lowercase hexadecimal digits"
  }
}

Signing Up and Logging In

Signing a user up with a linked service and logging them in with that service uses the same POST request, in which the authData for the user is specified. For example, to sign up or log in with a user's Twitter account:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/users', json.dumps({
       "authData": {
         "twitter": {
           "id": "12345678",
           "screen_name": "ParseIt",
           "consumer_key": "SaMpLeId3X7eLjjLgWEw",
           "consumer_secret": "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
           "auth_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
           "auth_token_secret": "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
         }
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "authData": {
          "twitter": {
            "id": "12345678",
            "screen_name": "ParseIt",
            "consumer_key": "SaMpLeId3X7eLjjLgWEw",
            "consumer_secret": "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
            "auth_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
            "auth_token_secret": "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
          }
        }
      }' \
  https://api.parse.com/1/users

Parse then verifies that the provided authData is valid and checks to see if a user is already associated with this data. If so, it returns a status code of 200 OK and the details (including a sessionToken for the user):

Status: 200 OK
Location: https://api.parse.com/1/users/uMz0YZeAqc

With a response body like:

{
  "username": "Parse",
  "createdAt": "2012-02-28T23:49:36.353Z",
  "updatedAt": "2012-02-28T23:49:36.353Z",
  "objectId": "uMz0YZeAqc",
  "sessionToken": "samplei3l83eerhnln0ecxgy5",
  "authData": {
    "twitter": {
      "id": "12345678",
      "screen_name": "ParseIt",
      "consumer_key": "SaMpLeId3X7eLjjLgWEw",
      "consumer_secret": "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
      "auth_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
      "auth_token_secret": "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
    }
  }
}

If the user has never been linked with this account, you will instead receive a status code of 201 Created, indicating that a new user was created:

Status: 201 Created
Location: https://api.parse.com/1/users/uMz0YZeAqc

The body of the response will contain the objectId, createdAt, sessionToken, and an automatically-generated unique username. For example:

{
  "username": "iwz8sna7sug28v4eyu7t89fij",
  "createdAt": "2012-02-28T23:49:36.353Z",
  "objectId": "uMz0YZeAqc",
  "sessionToken": "samplei3l83eerhnln0ecxgy5"
}

Linking

Linking an existing user with a service like Facebook or Twitter uses a PUT request to associate authData with the user. For example, linking a user with a Facebook account would use a request like this:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/users/uMz0YZeAqc', json.dumps({
       "authData": {
         "facebook": {
           "id": "123456789",
           "access_token": "SaMpLeAAibS7Q55FSzcERWIEmzn6rosftAr7pmDME10008bWgyZAmv7mziwfacNOhWkgxDaBf8a2a2FCc9Hbk9wAsqLYZBLR995wxBvSGNoTrEaL",
           "expiration_date": "2012-02-28T23:49:36.353Z"
         }
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "samplei3l83eerhnln0ecxgy5",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: samplei3l83eerhnln0ecxgy5" \
  -H "Content-Type: application/json" \
  -d '{
        "authData": {
          "facebook": {
            "id": "123456789",
            "access_token": "SaMpLeAAibS7Q55FSzcERWIEmzn6rosftAr7pmDME10008bWgyZAmv7mziwfacNOhWkgxDaBf8a2a2FCc9Hbk9wAsqLYZBLR995wxBvSGNoTrEaL",
            "expiration_date": "2012-02-28T23:49:36.353Z"
          }
        }
      }' \
  https://api.parse.com/1/users/uMz0YZeAqc

After linking your user to a service, you can authenticate them using matching authData.

Unlinking

Unlinking an existing user with a service also uses a PUT request to clear authData from the user by setting the authData for the service to null. For example, unlinking a user with a Facebook account would use a request like this:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/users/uMz0YZeAqc', json.dumps({
       "authData": {
         "facebook": null
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "samplei3l83eerhnln0ecxgy5",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: samplei3l83eerhnln0ecxgy5" \
  -H "Content-Type: application/json" \
  -d '{
        "authData": {
          "facebook": null
        }
      }' \
  https://api.parse.com/1/users/uMz0YZeAqc

Security

When you access Parse via the REST API key, access can be restricted by ACL just like in the iOS and Android SDKs. You can still read and modify acls via the REST API, just be accessing the "ACL" key of an object.

The ACL is formatted as a JSON object where the keys are either object ids or the special key "*" to indicate public access permissions. The values of the ACL are "permission objects", JSON objects whose keys are the permission names and whose values are always true.

For example, if you want the user with id "3KmCvT7Zsb" to have read and write access to an object, plus the object should be publicly readable, that corresponds to an ACL of:

{
  "3KmCvT7Zsb": {
    "read": true,
    "write": true
  },
  "*": {
    "read": true
  }
}

If you want to access your data ignoring all ACLs, you can use the master key provided on the Dashboard. Instead of the X-Parse-REST-API-Key header, set the X-Parse-Master-Key header. For backward compatibility, you can also do master-level authentication using HTTP Basic Auth, passing the application id as the username and the master key as the password. For security, the master key should not be distributed to end users, but if you are running code in a trusted environment, feel free to use the master key for authentication.

Roles

As your app grows in scope and user-base, you may find yourself needing more coarse-grained control over access to pieces of your data than user-linked ACLs can provide. To address this requirement, Parse supports a form of Role-based Access Control. Roles provide a logical way of grouping users with common access privileges to your Parse data. Roles are named objects that contain users and other roles. Any permission granted to a role is implicitly granted to its users as well as to the users of any roles that it contains.

For example, in your application with curated content, you may have a number of users that are considered "Moderators" and can modify and delete content created by other users. You may also have a set of users that are "Administrators" and are allowed all of the same privileges as Moderators, but can also modify the global settings for the application. By adding users to these roles, you can ensure that new users can be made moderators or administrators, without having to manually grant permission to every resource for each user.

We provide a specialized role class to represent these groupings of users for the purposes of assigning permissions. Roles have a few special fields that set them apart from other objects.

  • name: The name for the role. This value is required, and can only be set once as a role is being created. The name must consist of alphanumeric characters, spaces, -, or _. This name will be used to identify the Role without needing its objectId.
  • users: A relation to the set of users that will inherit permissions granted to the containing role.
  • roles: A relation to the set of child roles whose users and roles will inherit permissions granted to the containing role.

Often, in order to keep these roles secure, your mobile apps won't be directly responsible for managing creation and membership of your roles. Instead, roles may be managed by a separate interface on the web or manually managed by an administrator. Our REST API allows you to manage your roles without requiring a mobile client.

Creating Roles

Creating a new role differs from creating a generic object in that the name field is required. Roles must also specify an ACL, which should be as restrictive as possible to avoid allowing the wrong users to modify a role.

To create a new role, send a POST request to the roles root:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/roles', json.dumps({
       "name": "Moderators",
       "ACL": {
         "*": {
           "read": True
         }
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Moderators",
        "ACL": {
          "*": {
            "read": true
          }
        }
      }' \
  https://api.parse.com/1/roles

You can create a role with child roles or users by adding existing objects to the roles and users relations:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/roles', json.dumps({
       "name": "Moderators",
       "ACL": {
         "*": {
           "read": True
         }
       },
       "roles": {
         "__op": "AddRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "_Role",
             "objectId": "Ed1nuqPvc"
           }
         ]
       },
       "users": {
         "__op": "AddRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "_User",
             "objectId": "8TOXdXf3tz"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Moderators",
        "ACL": {
          "*": {
            "read": true
          }
        },
        "roles": {
          "__op": "AddRelation",
          "objects": [
            {
              "__type": "Pointer",
              "className": "_Role",
              "objectId": "Ed1nuqPvc"
            }
          ]
        },
        "users": {
          "__op": "AddRelation",
          "objects": [
            {
              "__type": "Pointer",
              "className": "_User",
              "objectId": "8TOXdXf3tz"
            }
          ]
        }
      }' \
  https://api.parse.com/1/roles

When the creation is successful, the HTTP response is a 201 Created and the Location header contains the object URL for the new object:

Status: 201 Created
Location: https://api.parse.com/1/roles/mrmBZvsErB

The response body is a JSON object containing the objectId and createdAt timestamp of the newly-created object:

{
  "createdAt": "2012-04-28T17:41:09.106Z",
  "objectId": "mrmBZvsErB"
}

Retrieving Roles

You can also retrieve the contents of a role object by sending a GET request to the URL returned in the location header when it was created. For example, to retrieve the role created above:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/roles/mrmBZvsErB', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/roles/mrmBZvsErB

The response body is a JSON object containing all of the fields on the role:

{
  "createdAt": "2012-04-28T17:41:09.106Z",
  "objectId": "mrmBZvsErB",
  "updatedAt": "2012-04-28T17:41:09.106Z",
  "ACL": {
    "*": {
      "read": true
    },
    "role:Administrators": {
      "write": true
    }
  },
  "name": "Moderators"
}

Note that the users and roles relations will not be visible in this JSON. Instead, you must query for the roles and users that belong to a given role using the $relatedTo operator.

Updating Roles

Updating a role generally works like updating any other object, but the name field on the role cannot be changed. Adding and removing users and roles to the users and roles relations can be accomplished by using the AddRelation and RemoveRelation operators.

For example, we can add two users to the "Moderators" role created above like so:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/roles/mrmBZvsErB', json.dumps({
       "users": {
         "__op": "AddRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "_User",
             "objectId": "8TOXdXf3tz"
           },
           {
             "__type": "Pointer",
             "className": "_User",
             "objectId": "g7y9tkhB7O"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "users": {
          "__op": "AddRelation",
          "objects": [
            {
              "__type": "Pointer",
              "className": "_User",
              "objectId": "8TOXdXf3tz"
            },
            {
              "__type": "Pointer",
              "className": "_User",
              "objectId": "g7y9tkhB7O"
            }
          ]
        }
      }' \
  https://api.parse.com/1/roles/mrmBZvsErB

Similarly, we can remove a child role from the "Moderators" role created above like so:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/roles/mrmBZvsErB', json.dumps({
       "roles": {
         "__op": "RemoveRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "_Role",
             "objectId": "Ed1nuqPvc"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "roles": {
          "__op": "RemoveRelation",
          "objects": [
            {
              "__type": "Pointer",
              "className": "_Role",
              "objectId": "Ed1nuqPvc"
            }
          ]
        }
      }' \
  https://api.parse.com/1/roles/mrmBZvsErB

Note that we've included the master key in the query above because the "Moderators" role has an ACL that restricts modification by the public.

Deleting Roles

To delete a role from the Parse Cloud, send a DELETE request to its URL. For example:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/roles/mrmBZvsErB', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  https://api.parse.com/1/roles/mrmBZvsErB

Again, we pass the master key in order to bypass the ACL on the role itself. Alternatively, we could pass an X-Parse-Session-Token for a user that has write access to the Role object (e.g. an Administrator). For example:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/roles/mrmBZvsErB', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "X-Parse-Session-Token": "pnktnjyb996sj4p156gjtp4im"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "X-Parse-Session-Token: pnktnjyb996sj4p156gjtp4im" \
  https://api.parse.com/1/roles/mrmBZvsErB

Security

When you access Parse via the REST API key, access can be restricted by ACL just like in the iOS and Android SDKs. You can still read and modify ACLs via the REST API, just by accessing the "ACL" key of an object.

In addition to per-user permissions as described above, you can also specify role-level permissions to your Parse objects. Instead of specifying an objectId as the key for a permission object as you do for users, you can instead specify a role's name with a "role:" prefix as the key for a permission object. You can use role-level permissions alongside user-level permissions to provide fine-grained control over user access.

For example, to restrict an object to be readable by anyone in the "Members" role and writable by its creator and anyone in the "Moderators" role, you would specify an ACL like this:

{
  "8TOXdXf3tz": {
    "write": true
  },
  "role:Members": {
    "read": true
  },
  "role:Moderators": {
    "write": true
  }
}

You are not required to specify read permissions for the user or the "Moderators" role if the user and role are already children of the "Members" role, since they will inherit read permissions granted to "Members".

Role Hierarchy

As described above, one role can contain another, establishing a parent-child relationship between the two roles. The consequence of this relationship is that any permission granted to the parent role is implicitly granted to all of its child roles.

These types of relationships are commonly found in applications with user-managed content, such as forums. Some small subset of users are "Administrators", with the highest level of access to tweaking the application's settings, creating new forums, setting global messages, and so on. Another set of users are "Moderators", who are responsible for ensuring that the content created by users remains appropriate. Any user with Administrator privileges should also be granted the permissions of any Moderator. To establish this relationship, you would make your "Administrators" role a child role of "Moderators" by adding the "Administrators" role to the roles relation on your "Moderators" object like this:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/roles/<ModeratorsRoleObjectId>', json.dumps({
       "roles": {
         "__op": "AddRelation",
         "objects": [
           {
             "__type": "Pointer",
             "className": "_Role",
             "objectId": "<AdministratorsRoleObjectId>"
           }
         ]
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "roles": {
          "__op": "AddRelation",
          "objects": [
            {
              "__type": "Pointer",
              "className": "_Role",
              "objectId": "<AdministratorsRoleObjectId>"
            }
          ]
        }
      }' \
  https://api.parse.com/1/roles/<ModeratorsRoleObjectId>

Files

Uploading Files

To upload a file to Parse, send a POST request to the files URL, postfixed with the name of the file. The request must contain the Content-Type header associated with the file. Keep in mind that files are limited to 10 megabytes. Here's a simple example that'll create a file named hello.txt containing a string:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/files/hello.txt', 'Hello, World!', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "text/plain"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: text/plain" \
  -d 'Hello, World!' \
  https://api.parse.com/1/files/hello.txt

When the file upload is successful, the HTTP response is a 201 Created and the Location header which contains the URL for the file:

Status: 201 Created
Location: http://files.parse.com/bc9f32df-2957-4bb1-93c9-ec47d9870a05/db295fb2-8a8b-49f3-aad3-dd911142f64f-hello.txt

The response body is a JSON object containing the name of the file, which is the original file name prefixed with a unique identifier in order to prevent name collisions. This means you can save files with the same name, and the files will not overwrite one another.

{
  "url": "http://files.parse.com/bc9f32df-2957-4bb1-93c9-ec47d9870a05/db295fb2-8a8b-49f3-aad3-dd911142f64f-hello.txt",
  "name": "db295fb2-8a8b-49f3-aad3-dd911142f64f-hello.txt"
}

To upload an image, the syntax is a little bit different. Here's an example that will upload the image myPicture.jpg from the current directory.

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/files/pic.jpg', open('myPicture.jpg', 'rb').read(), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "image/jpeg"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: image/jpeg" \
  --data-binary '@myPicture.jpg' \
  https://api.parse.com/1/files/pic.jpg

Associating with Objects

After files are uploaded, you can associate them with Parse objects:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/classes/PlayerProfile', json.dumps({
       "name": "Andrew",
       "picture": {
         "name": "...profile.png",
         "__type": "File"
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Andrew",
        "picture": {
          "name": "...profile.png",
          "__type": "File"
        }
      }' \
  https://api.parse.com/1/classes/PlayerProfile

Note that the name of the file in the request is not the local file name, but the name in the response of the previous upload operation.

Deleting Files

Users holding the master key are allowed to delete files using the REST API. To delete a file, send a DELETE request to the files URL, postfixed with the name of the file. Note that the name of the file must be the name in the response of the upload operation, rather than the original filename. Note that the X-Parse-Master-Key must be provided in headers.

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/files/...profile.png', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  https://api.parse.com/1/files/...profile.png

Analytics

Parse provides a number of hooks for you to get a glimpse into the ticking heart of your app. We understand that it's important to understand what your app is doing, how frequently, and when.

While this section will cover different ways to instrument your app to best take advantage of Parse's analytics backend, developers using Parse to store and retrieve data can already take advantage of metrics on Parse.

Without having to implement any client-side logic, you can view real-time graphs and breakdowns (by device type, Parse class name, or REST verb) of your API Requests in your app's dashboard and save these graph filters to quickly access just the data you're interested in.

App-Open Analytics

Our analytics hook allows you to track your application being launched. By making a POST request to our REST API, you'll begin to collect data on when and how often your application is opened.

In the example below, the at parameter is optional. If omitted, the current server time will be used instead.

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/events/AppOpened', json.dumps({
       "at": {
         "__type": "Date",
         "iso": "2014-04-17T23:28:41Z"
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "at": {
          "__type": "Date",
          "iso": "2014-04-17T23:28:41Z"
        }
      }' \
  https://api.parse.com/1/events/AppOpened

Graphs and breakdowns of your statistics are accessible from your app's Dashboard.

Custom Analytics

Parse Analytics also allows you to track free-form events, with a handful of string keys and values. These extra dimensions allow segmentation of your custom events via your app's Dashboard.

Say your app offers search functionality for apartment listings, and you want to track how often the feature is used, with some additional metadata.

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/events/Search', json.dumps({
       "at": {
         "__type": "Date",
         "iso": "2014-04-17T23:28:41Z"
       },
       "dimensions": {
         "priceRange": "1000-1500",
         "source": "craigslist",
         "dayType": "weekday"
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "at": {
          "__type": "Date",
          "iso": "2014-04-17T23:28:41Z"
        },
        "dimensions": {
          "priceRange": "1000-1500",
          "source": "craigslist",
          "dayType": "weekday"
        }
      }' \
  https://api.parse.com/1/events/Search

Parse Analytics can even be used as a lightweight error tracker — simply invoke the following and you'll have access to an overview of the rate and frequency of errors, broken down by error code, in your application:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/events/Error', json.dumps({
       "at": {
         "__type": "Date",
         "iso": "2014-04-17T23:28:41Z"
       },
       "dimensions": {
         "code": "404"
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "at": {
          "__type": "Date",
          "iso": "2014-04-17T23:28:41Z"
        },
        "dimensions": {
          "code": "404"
        }
      }' \
  https://api.parse.com/1/events/Error

Note that Parse currently only stores the first eight dimension pairs per call to /1/events/<eventName>.

Push Notifications

To learn more about push check out our Push Notification Guide!

Installations

Uploading Installation Data

An installation object represents an instance of your app being installed on a device. These objects are used to store subscription data for installations which have subscribed to one or more push notification channels. Installations have a flexible schema, except that the special fields below have special type and validation requirements:

  • deviceType is a required string field that must be set to either "ios" or "android". It may not be changed once the object is created.
  • installationId is a Parse-generated string identifier that is required for devices with a deviceType of "android" and optional for devices with a deviceType of "ios". It may not be changed once the object is created and must be unique across all of an app's installations.
  • deviceToken is an Apple-generated string identifier that is required for devices with a deviceType of "ios". It may not be changed once the object is created and must be unique across all of an app's installations.
  • badge is a number field representing the last known application badge for iOS installations.
  • timeZone is a string field representing the system time zone of the device running this installation.
  • channels is an optional array of strings representing the subscriptions of this installation object.

Most of the time, installation data is modified by push-related methods in the client SDK. For example, calling subscribeToChannel or unsubscribeFromChannel from the client SDK will create an object for that installation if it doesn't yet exist and update its channels, and calling getSubscribedChannels from the client SDK will read subscription data from that installation's object. The REST methods can be used to mimic these operations. For instance, if you have an iOS device token then you can subscribe it to push notifications by creating an installation object for it with the desired channels list. You can also perform operations which aren't possible through the client SDK, like using a query over installations to find the set of subscribers to a given channel.

Creating an installation object is similar to creating a generic object, but the special installation fields listed above must pass validation. For example, if you have a device token provided by the Apple Push Notification service and would like to subscribe it to the broadcast channel "", you can use the following command:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/installations', json.dumps({
       "deviceType": "ios",
       "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
       "channels": [
         ""
       ]
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "deviceType": "ios",
        "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
        "channels": [
          ""
        ]
      }' \
  https://api.parse.com/1/installations

When the creation is successful, the HTTP response is a 201 Created and the Location header contains the URL for the new installation:

Status: 201 Created
Location: https://api.parse.com/1/installations/mrmBZvsErB

The response body is a JSON object containing the objectId and the createdAt timestamp of the newly-created installation:

{
  "createdAt": "2012-04-28T17:41:09.106Z",
  "objectId": "mrmBZvsErB"
}

Retrieving Installations

You can retrieve the contents of an installation object by sending a GET request to the URL returned in the location header when it was created. For example, to retrieve the installation created above:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/installations/mrmBZvsErB', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/installations/mrmBZvsErB

The response body is a JSON object containing all the user-provided fields, plus the createdAt, updatedAt, and objectId fields:

{
  "deviceType": "ios",
  "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "channels": [
    ""
  ],
  "createdAt": "2012-04-28T17:41:09.106Z",
  "updatedAt": "2012-04-28T17:41:09.106Z",
  "objectId": "mrmBZvsErB"
}

Updating Installations

Installation objects can be updated by sending a PUT request to the installation URL. For example, to subscribe the installation above to the "foo" push channel:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('PUT', '/1/installations/mrmBZvsErB', json.dumps({
       "deviceType": "ios",
       "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
       "channels": [
         "",
         "foo"
       ]
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X PUT \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "deviceType": "ios",
        "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
        "channels": [
          "",
          "foo"
        ]
      }' \
  https://api.parse.com/1/installations/mrmBZvsErB

Querying Installations

You can retrieve multiple installations at once by sending a GET request to the root installations URL. This functionality is not available in the SDKs, so you must authenticate this method using the X-Parse-Master-Key header in your request instead of the X-Parse-REST-API-Key header. Your master key allows you to bypass ACLs and should only be used from within a trusted environment.

Without any URL parameters, a GET request simply lists installations:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('GET', '/1/installations', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  https://api.parse.com/1/installations

The return value is a JSON object that contains a results field with a JSON array that lists the users.

{
  "results": [
    {
      "deviceType": "ios",
      "deviceToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
      "channels": [
        ""
      ],
      "createdAt": "2012-04-28T17:41:09.106Z",
      "updatedAt": "2012-04-28T17:41:09.106Z",
      "objectId": "mrmBZvsErB"
    },
    {
      "deviceType": "ios",
      "deviceToken": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
      "channels": [
        ""
      ],
      "createdAt": "2012-04-30T01:52:57.975Z",
      "updatedAt": "2012-04-30T01:52:57.975Z",
      "objectId": "sGlvypFQcO"
    }
  ]
}

All of the options for queries that work for regular objects also work for installation objects, so check the section on Querying Objects for more details. By doing an array query over channels, for example, you can find the set of devices subscribed to a given push channel.

Deleting Installations

To delete an installation from the Parse Cloud, send a DELETE request to its URL. This functionality is not available in the client SDKs, so you must authenticate this method using the X-Parse-Master-Key header in your request instead of the X-Parse-REST-API-Key header. Your master key allows you to bypass ACLs and should only be used from within a trusted environment. For example:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('DELETE', '/1/installations/mrmBZvsErB', '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  https://api.parse.com/1/installations/mrmBZvsErB

Cloud Code

Cloud Functions

Cloud Functions can be called using the REST API. For example, to call the Cloud Function named hello:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/functions/hello', json.dumps({
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://api.parse.com/1/functions/hello

Background Jobs

Similarly, you can trigger a background job from the REST API. For example, to trigger the job named userMigration:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/jobs/userMigration', json.dumps({
       "plan": "paid"
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-Master-Key": "${MASTER_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-Master-Key: ${MASTER_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"plan":"paid"}' \
  https://api.parse.com/1/jobs/userMigration
Take a look at the Cloud Code Guide to learn more about Cloud Functions and Background Jobs.

GeoPoints

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a GeoPoint data type to a class allows queries to take into account the proximity of an object to a reference point. This allows you to easily do things like find out what user is closest to another user or which places are closest to a user.

GeoPoint

To associate a point with an object you will need to embed a GeoPoint data type into your object. This is done by using a JSON object with __type set to the string GeoPoint and numeric values being set for the latitude and longitude keys. For example, to create an object containing a point under the "location" key with a latitude of 40.0 degrees and -30.0 degrees longitude:

import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/classes/PlaceObject', json.dumps({
       "location": {
         "__type": "GeoPoint",
         "latitude": 40.0,
         "longitude": -30.0
       }
     }), {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}",
       "Content-Type": "application/json"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "location": {
          "__type": "GeoPoint",
          "latitude": 40.0,
          "longitude": -30.0
        }
      }' \
  https://api.parse.com/1/classes/PlaceObject

Geo Queries

Now that you have a bunch of objects with spatial coordinates, it would be nice to find out which objects are closest to a point. This can be done by using a GeoPoint data type with query on the field using $nearSphere. Getting a list of ten places that are closest to a user may look something like:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"limit":10,"where":json.dumps({
       "location": {
         "$nearSphere": {
           "__type": "GeoPoint",
           "latitude": 30.0,
           "longitude": -20.0
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/PlaceObject?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'limit=10' \
  --data-urlencode 'where={
        "location": {
          "$nearSphere": {
            "__type": "GeoPoint",
            "latitude": 30.0,
            "longitude": -20.0
          }
        }
      }' \
  https://api.parse.com/1/classes/PlaceObject

This will return a list of results ordered by distance from 30.0 latitude and -20.0 longitude. The first result will be the nearest object. (Note that if an explicit order parameter is supplied, it will take precedence over the distance ordering.) For example, here are two results returned for the above query:

{
  "results": [
    {
      "location": {
        "latitude": 40.0,
        "__type": "GeoPoint",
        "longitude": -30.0
      },
      "updatedAt": "2011-12-06T22:36:04.983Z",
      "createdAt": "2011-12-06T22:36:04.983Z",
      "objectId": "iFEPN5Gwoz"
    },
    {
      "location": {
        "latitude": 60.0,
        "__type": "GeoPoint",
        "longitude": -20.0
      },
      "updatedAt": "2011-12-06T22:36:26.143Z",
      "createdAt": "2011-12-06T22:36:26.143Z",
      "objectId": "LAyNKSNTHT"
    }
  ]
}

To limit the search to a maximum distance add a $maxDistanceInMiles (for miles), $maxDistanceInKilometers (for kms), or $maxDistanceInRadians (for radian angle), term to the key constraint. For example, the following limits the radius to 10 miles:

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "location": {
         "$nearSphere": {
           "__type": "GeoPoint",
           "latitude": 30.0,
           "longitude": -20.0
         },
         "$maxDistanceInMiles": 10.0
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/PlaceObject?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={
        "location": {
          "$nearSphere": {
            "__type": "GeoPoint",
            "latitude": 30.0,
            "longitude": -20.0
          },
          "$maxDistanceInMiles": 10.0
        }
      }' \
  https://api.parse.com/1/classes/PlaceObject

It's also possible to query for the set of objects that are contained within a particular area. To find the objects in a rectangular bounding box, add a clause to the key constraint with the format {"$within": {"$box": {[southwestGeoPoint, northeastGeoPoint]}}}.

import json,httplib,urllib
connection = httplib.HTTPSConnection('api.parse.com', 443)
params = urllib.urlencode({"where":json.dumps({
       "location": {
         "$within": {
           "$box": [
             {
               "__type": "GeoPoint",
               "latitude": 37.71,
               "longitude": -122.53
             },
             {
               "__type": "GeoPoint",
               "latitude": 30.82,
               "longitude": -122.37
             }
           ]
         }
       }
     })})
connection.connect()
connection.request('GET', '/1/classes/PizzaPlaceObject?%s' % params, '', {
       "X-Parse-Application-Id": "${APPLICATION_ID}",
       "X-Parse-REST-API-Key": "${REST_API_KEY}"
     })
result = json.loads(connection.getresponse().read())
print result
curl -X GET \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -G \
  --data-urlencode 'where={
        "location": {
          "$within": {
            "$box": [
              {
                "__type": "GeoPoint",
                "latitude": 37.71,
                "longitude": -122.53
              },
              {
                "__type": "GeoPoint",
                "latitude": 30.82,
                "longitude": -122.37
              }
            ]
          }
        }
      }' \
  https://api.parse.com/1/classes/PizzaPlaceObject

Caveats

At the moment there are a couple of things to watch out for:

  1. Each PFObject class may only have one key with a PFGeoPoint object.
  2. Points should not equal or exceed the extreme ends of the ranges. Latitude should not be -90.0 or 90.0. Longitude should not be -180.0 or 180.0. Attempting to use GeoPoint's with latitude and/or longitude outside these ranges will cause an error.