API Reference (deprecated)

These endpoints have been deprecated. While they are are still suppported, we encourage you to check out our new API endpoints!

API requests use OAuth 2.0 bearer tokens over SSL, in the Authorization header (read through our authorization reference to find out how to get a token. See here for a list of supported scopes).
If you get a non-200 response, check this list of errors.

Your token gets genetic data from the customer who authorized it. If the "customer" was a demo account and not genotyped, you can still get mock genetic data (from two anonymous humans) by prepending /demo/ to most endpoints. For example:

curl https://api.23andme.com/1/user/ -H "Authorization: Bearer 081239h0f71hf" # customer's real data
curl https://api.23andme.com/1/demo/user/ -H "Authorization: Bearer 081239h0f71hf" # anonymized data

Endpoints

endpoint scope required example call and result
GET /1/user/
GET /1/demo/user/
Back To Top ↑
basic

email(opt)

Gets the user id, and a list of profiles (an account can have multiple genotyped people) with ids, whether or not they're genotyped.

The optional email parameter can be used to request the email for this account. Requires the "email" scope.

The optional services parameter can be used to request the endpoint to return what services are available to the profiles.

A service model object is made up of a unique service id, a service label, and a user-readable description of the service. All genotyped profiles should have "pgs_ancestry". Customers with access to health data will have "pgs_health". Demo profiles will have no services.

This endpoint is great for using an app anonymously because there is no personally identifying information.

curl https://api.23andme.com/1/user/ -H "..."
# JSON response:
{
    "id": "a42e94634e3f7683",
    "profiles": [
        {
            "genotyped": true,
            "id": "c4480ba411939067"
        }, ...
    ]
}

?services=true
# JSON response:
{
    "id": "a42e94634e3f7683",
    "profiles": [
        {
            "genotyped": true,
            "id": "c4480ba411939067",
            "services": [
                {
                    "description": "Health",
                    "label": "pgs_health",
                    "id": 1
                }, ...
            ]
        }, ...
    ]
}

?email=true w/ email scope
#JSON response:
{
    "id": "a42e94634e3f7683",
    "email": "gregormendel@23andme.com",
    "profiles": [
        {
            "genotyped": true,
            "id": "c4480ba411939067"
        }, ...
    ]
}
GET /1/names/profile_id/
GET /1/demo/names/profile_id/
Back To Top ↑
names For the user and user's profile, gets first and last names. If your user wants to remain anonymous, you shouldn't request this scope. You can optionally filter by profile_id to get the names for just that profile.
curl https://api.23andme.com/1/names/ -H "..."
# JSON response:
{
    "first_name": "Gregor",
    "last_name": "Mendel",
    "id": "a42e94634e3f7683",
    "profiles": [
        {
            "first_name": "Johann",
            "last_name": "Mendel",
            "id": "c4480ba411939067"
        }, ...
    ]
}
GET /1/profile_picture/profile_id/
POST /1/profile_picture/profile_id/
Back To Top ↑
We're no longer supporting this endpoint, please use the v3 profile endpoint
GET /1/genotypes/profile_id/?locations=&unfiltered=&format=...
GET /1/demo/genotypes/profile_id/?locations=&unfiltered=&format=...
Back To Top ↑
rsXX for each SNP requested, or genomes For the user's profile, returns the base-pairs, like AA, for the given locations. The value can have Ds or Is for deletions and insertions (for example, DD or DI). It can be __ if the customer is not on a chip that calls that location, or hasn't yet unlocked their call since it corresponds to a sensitive report. It can be -- if the customer is on a chip that calls that location, but we could not determine it. To keep consistency with the /genomes endoint, which always returns two base pairs, hemizygous calls (such as on X-linked genes in males) will also return two base pairs.

The scope of your token must include each location you request (i.e., getting the below data requires a scope of at least rs3094315 i3000001). This list of SNPs (44MB) shows which SNPs our customers are genotyped for.

The unfiltered parameter can be used for completely sex-unfiltered data.

The format parameter can be used to alter the JSON output format of this endpoint.

Since this is a GET endpoint, you may hit the browser-imposed URL limit with a lot of SNPs. We recommend splitting your requests into 100-SNP chunks.
curl https://api.23andme.com/1/genotypes/c44.../?locations=rs3094315%20i3000001 -H "..."
# JSON response:
{
    "i3000001": "II",
    "rs3094315": "AA"
    "id": "c4480ba411939067"
}

?format=embedded
# JSON response:
{
    "id": "c4480ba411939067"
    "genotypes": [
        {
            "location": "i3000001",
            "call": "II"
        },
        {
            "location": "rs3094315",
            "call": "AA"
        }
    ]
}
 
GET /1/phenotypes/profile_id/phenotype_id/
Back To Top ↑
phenotypes:read:<phenotype_id>

E.g. phenotypes:read:sex
For the user's profile, returns the requested phenotype. Which phenotypes can I get?
curl https://api.23andme.com/1/phenotypes/c44.../sex/
     -H "..."
# JSON response:
{
    "phenotype_id": "sex",
    "value": "male",
    "profile_id": "c4480ba411939067"
}
PUT /1/phenotypes/profile_id/phenotype_id/
Back To Top ↑
phenotypes:write:<phenotype_id>

E.g. phenotypes:write:date_of_birth
For the user's profile, sets the requested phenotype data. Which phenotypes can I get? The response will be identical to the GET so that you can verify the data is set properly.
curl -X PUT https://api.23andme.com/1/phenotypes/c44.../date_of_birth/
     -d "value=1976-06-30"
     -H "..."
# JSON response:
{
    "phenotype_id": "date_of_birth",
    "value": "1976-06-30",
    "profile_id": "c4480ba411939067"
}
GET /1/genomes/profile_id/
GET /1/demo/genomes/profile_id/
Back To Top ↑
genomes Returns the entire profile's genome as a packed string of base pairs "AACTGA...". This ~2MB request returns over a million SNP locations, so you must specify profile_id. See the /genotypes endpoint for possible values. Each SNP is represented with two base pairs, and to know which SNP corresponds to which index, see this key.

The endpoint may return a result with "status": "PENDING" and "genome": null due to internal caching. If this happens, the result should be ready in a few minutes.

When our genotyping chip is upgraded, the packed string and corresponding key will grow, but the changes will be backwards-compatible additions.
curl https://api.23andme.com/1/genomes/c44.../ -H "..."
# JSON response:
{
    "id": "c4480ba411939067",
    "genome": "ACTAGTAG__TTGADDAAIICCTT", # ... (2MB string!)
    "status": "READY"
}
# "Not ready" response:
{
    "id": "c4480ba411939067",
    "genome": null,
    "status": "PENDING"
}
 
GET /1/haplogroups/profile_id/
GET /1/demo/haplogroups/profile_id/
Back To Top ↑
haplogroups For the user's profile, gets maternal and paternal haplogroups, as well as terminal SNPs. Maternal terminal SNPs include the rsid and rCRS reference position, while the paternal ones include the rsid and ISOGG SNP.

Note: if the profile belongs to a female, the paternal (y) haplogroup and terminal SNP information will be null.
curl https://api.23andme.com/1/haplogroups/c44.../ -H "..."
# JSON response:
{
    "maternal_terminal_snps": [
        {
            "rsid": "i3001424",
            "rcrs_position": "15874"
        },
        {
            "rsid": "i5050411",
            "rcrs_position": "15874"
        }
    ],
    "paternal_terminal_snps": [
        {
            "rsid": "i3000015",
            "snp": "M125"
        }
    ],
    "maternal": "D4e2",
    "paternal": "D2a1",
    "id": "c4480ba411939067"
}
GET /1/ancestry/profile_id/?threshold=...
GET /1/demo/ancestry/profile_id/?threshold=...
Back To Top ↑
ancestry Ancestral breakdown for the user's profile. Each population has an absolute proportion of the genome. A population with sub_populations has an unassigned proportion — the ancestry we couldn't classify in it.

threshold is optional, default 0.51 and range (0.5, 1.0) exclusive. 0.51 means a speculative estimate, 0.75 standard, and 0.90 conservative. A higher threshold would increase the unassigned proportions, a lower would speculate.

If the user's ancestry hasn't been computed yet, you'll see "ancestry": null.
curl https://api.23andme.com/1/ancestry/c44.../?threshold=0.9 -H "..."
# JSON response:
{
    "id": "7ad467ea509080fb"
    "ancestry": {
        "label": "Total",
        "proportion": 1.0,
        "unassigned": 0.0,
        "sub_populations": [
            {
                "label": "Sub-Saharan African",
                "proportion": 0.8227
            },
            {
                "label": "European"
                "proportion": 0.1773,
                "unassigned": 0.0193,
                "sub_populations": [
                    {
                        "label": "Northwestern European",
                        "proportion": 0.1579,
                        "unassigned": 0.0725,
                        "sub_populations": [
                            {
                                "label": "French and German",
                                "proportion": 0.0676
                            }, ...
                        ],
                    }, ...
                ]
            }, ...
        ]
    }
}
GET /1/family_members/?limit=x&offset=y
GET /1/demo/family_members/?limit=x&offset=y
Back To Top ↑
family_tree

names(opt)

All Family Members in an account's family tree as entered in the family tree tool. Every profile has a family tree member node created automatically, so even users who have never used the family tree tool will have some data here.

The response is formed to return the account id, and pagination information limit offset and count. Since the account can have thousands of family tree members, limit defaults to 10, and offset to 0. You can override the limit (or not limit it at all with limit = 0, but be careful, the user may have thousands of family tree members). count gives the total number of matches after filtering. The limit and offset values in the response will represent the pagination values used by this request.

Family Members:
A family member in the members array will have a member id field id. Any family member that is associated with a profile will also have non empty profile_id. The sex field can be set to "m" or "f" to represent male and female respectively. The adopted and deceased fields will both contain true or false. The first_name, middle_name, last_name, birth_surname, name_suffix and nickname fields will only be sent if the account has given you the 'names' scope. The partners and parents fields are arrays of member ids. Finally, the events field contains an array of events for the family member as shown in the example below.

curl https://api.23andme.com/1/family_members/ -H "..."
# JSON response:
{
    "id": "a42e94634e3f7683"
    "limit": 10,
    "offset": 0,
    "count": 23,
    "members": [
        {
            "id": "48f2489h294hf",
            "profile_id": "8cb31789fb47912c",
            "first_name": "Ian",
            "middle_name": null,
            "last_name": "Mendel",
            "birth_surname": null,
            "name_suffix": "Jr",
            "nickname": "Junior",
            "sex": "m",
            "adopted": false,
            "deceased": false,
            "partners": [
                "c4480ba411939067"
            ],
            "parents": [
                "b5d5423af9a82848",
                "e07427ff8628a8ce"
            ],
            "events": [
                {
                    "type": "birth",
                    "date": "1943-11-29",
                    "end_date": null,
                    "location": "New Orleans, LA"
                },
                {
                    "type": "residence",
                    "date": "1956-05-16",
                    "end_date": "1969-07-16",
                    "location": "Chicago, IL"
                }
            ]
        }
        ...
    ]
}
GET /1/neanderthal/profile_id/
GET /1/demo/neanderthal/profile_id/
Back To Top ↑
ancestry Estimated genome-wide proportion of Neanderthal ancestry for the user's profile. Most users have between 0.01 and 0.04 Neanderthal ancestry -- read a full explanation of the science. proportion is -1 for un-genotyped (or as-of-yet uncomputed) profiles.
curl https://api.23andme.com/1/neanderthal/c44.../ -H "..."
# JSON response:
{
    "id": "7ad467ea509080fb"
    "neanderthal": {
        "proportion": 0.0310,
        "ancestry": "East Asian",
        "average": 0.028,
        "percentile": 20
    }
}
GET /1/relatives/profile_id/?limit=x&offset=y
&since=1348699925&share_status=z
Back To Top ↑
relatives Relatives on 23andMe, for the user's profile. shared_segments is the total number of shared IBD segments; similarity is the actual proportion (1.00 being you, or an identical twin). maternal/paternal_side are True if this match is a relative from your mom or dad's side. range is defined if we couldn't pinpoint the relatedness of the match.

match_id is a unique identifier for matches for a given profile, but not across profiles. For example, if profile A has a relative C1, and profile B has relative C2, then any C1.match_id = C2.match_id is coincidental and does not mean C1 and C2 represent the same person. We cannot expose globally unique match_ids. match_id is null if the match is you.

Since you could have thousands of matches, limit defaults to 10, and offset to 0. You can override the limit (or not limit it at all with limit = 0, but be careful, the user may have thousands of relatives). count gives the total number of matches after filtering. Results are sorted by updated, descending. You can also get results that have been updated or added since a timestamp.

You can also filter matches by their share_status. Note that you will have to URL-encode these parameters (i.e., Sharing%20Genomes). The possible values are:

Owned Profile
Sharing Genomes
Public Match

You can provide an optional match_id parameter to limit the results returned to that of an individual match. In this case, match_id overrides any other conflicting parameters. For instance, if you provide both a match_id and a since parameter, the match's information will be returned regardless of whether the match was updated or added since the timestamp specified by the since parameter. The count returned will be 1 when the match_id parameter is specified. Example usage is as follows: https://api.23andme.com/1/relatives/c44.../?match_id=48f...
curl https://api.23andme.com/1/relatives/c44.../ -H "..."
# JSON response:
{
    "id": "18974891hh1f3h",
    "count": 10,
    "relatives": [
        {
            "match_id": "48f2489h294hf",
            "first_name": "Aodh",
            "last_name": "O'Donnell",
            "sex": "Male",
            "birth_year": 1977,
            "birthplace": "United States",
            "ancestry": "Northwestern Europe",
            "family_locations": [
                "Arlington, VA",
                "County Louth, Ireland"
            ],
            "family_surnames": [
                "Lindell",
                "Dillingham",
                "Kelly"
            ],
            "shared_segments": 23,
            "relationship": "3rd Cousin",
            "predicted_relationship_code": 32,
            "user_relationship_code": null,
            "range": ["3rd Cousin", "6th Cousin"],
            "similarity": 0.24,
            "maternal_haplogroup": "K1b1a1",
            "paternal_haplogroup": "G2a5",
            "maternal_side": false,
            "paternal_side": true,
            "notes": "we have the same familiar surnames",
            "added": 1348699925,
            "updated": 1348699975,
            "residence": "North Carolina",
            "share_status": "Sharing Genomes",
        }, ...
    ]
}
PATCH /1/relatives/profile_id/match_id/
Back To Top ↑
relatives:write Updates a relative match. In our DNA Relatives feature, we calculate a predicted relationship based on your overlapping DNA segments. But if you know for sure, you can update the match with a known user_relationship_code (see below). You can also add notes about the match.

0 You
1 Identical Twin
2 Father
3 Mother
4 Son
5 Daughter
6 Brother
7 Sister
8 Half Brother
9 Half Sister
10 Grandfather
11 Grandmother
12 Grandson
13 Granddaughter
14 Uncle
15 Aunt
16 Nephew
17 Niece
18 Great-Grandfather
19 Great-Grandson
20 Great-Grandmother
21 Great-Granddaughter
22 Great-Uncle
23 Great-Aunt
24 Great-Nephew
25 Great-Niece
26 1st Cousin
27 1st Cousin, Once Removed
28 1st Cousin, Twice Removed
29 2nd Cousin
30 2nd Cousin, Once Removed
31 2nd Cousin, Twice Removed
32 3rd Cousin
33 3rd Cousin, Once Removed
34 3rd Cousin, Twice Removed
35 4th Cousin
38 5th Cousin
41 6th Cousin
44 Distant Cousin
curl -X PATCH https://api.23andme.com/1/relatives/c44.../48f24.../?notes=nice&user_relationship_code=33 -H "..."
# Response will be an empty HTTP 204 if successful

About the unfiltered parameter

For the /genotypes endpoint, the optional parameter unfiltered returns raw calls from our probes. It's unlikely you'd need it, unless you want to perform your own biological sex checks, or are developing a sex-specific app such as a check for Klinefelter's Syndrome. Without the parameter, we filter out calls based on the genotype's sex, but if ?unfiltered=true, we will not filter:

  • Y calls for women
  • Heterozygous X calls for men on the non-pseudoautosomal region
  • Heterozygous mitochondrial calls

List of phenotypes you can get/set

For the /phenotypes read and write endpoints, your phenotype_id can be any of the following:

  • bd_pgen_patient_id: Participation in Harvard (HSPH) study of personal genomics (PGen)
  • date_of_birth: date of birth (YYYY-MM-DD)
  • family_tree_url: family tree url
  • height_mm: height in millimeters
  • sex: sex
  • weight_g: weight in grams