The GIA Report Results API enables applications to integrate with GIA’s database to verify grading information and retrieve assets associated with a report.
Read this guide for details on how to query a report number and parse the response.
The GIA Report Results API uses GraphQL. GraphQL is a query language supported by all major programming languages.
GraphQL tools provide documentation navigators to help you explore the API. To learn more, try our Quickstart Guide.
The GIA Report Results API returns results for GIA’s reports and services through an endpoint called getReport
.
A call to getReport
returns a GradingReport
with fields common to all reports. These include report number, report date, and report type. Different report types have distinct fields in their results. For example, a diamond report has different attributes than a sapphire report.
The results
field contains a union type called ReportResults
that returns one of the following concrete types:
DiamondGradingReportResults
for natural diamond reports, such as Diamond Dossier, Diamond Grading, and Colored Diamond Grading reports.PearlIdentReportResults
for pearl reports.LabGrownDiamondGradingReportResults
for lab-grown diamond reports.IdentificationReportResults
for colored stones reports.MeleeServiceResults
for results on the Melee Analysis service.NarrativeReportResults
for results that do not apply to the other formats.Use a conditional fragment (such as ... on DiamondGradingReportResults
) to access these fields.
Tip: Use the __typename meta field to determine how to handle the data on your client.
{
getReport(report_number: "2141438171") {
report_date
report_number
report_type
results {
... on DiamondGradingReportResults {
__typename
shape_and_cutting_style
carat_weight
color_grade
clarity_grade
}
}
}
}
returns
{
"data": {
"getReport": {
"report_date": "September 01, 2019",
"report_number": "2141438171",
"report_type": "Diamond Dossier",
"results": {
"__typename": "DiamondGradingReportResults",
"shape_and_cutting_style": "Emerald Cut",
"carat_weight": "0.51 carat",
"color_grade": "E",
"clarity_grade": "VS2"
}
}
}
}
Most results fields are String types to accommodate the text as it appears on a grading report. For example, Internally Flawless
clarity grades will be spelled out rather than abbreviated IF
.
In most cases, abbreviated versions are also available. These Enums return one of the allowed values. Look for fields that end with _code
. Examples are report_type_code
, clarity_grade_code
, and color_grade_code
.
Concatenated fields are also available individually. For example, measurements for round diamonds are minimum diameter - maximum diameter x depth
. The numerical fields arise in the RoundMeasurements
type.
The links
field holds links to supplementary assets.
The assets
field also holds links to supplementary assets in an array strcuture format. The array structure format provides additional metadata such as asset_type_code
and asset_type_description
.
The availability of assets depends on the report type and other factors. Fields are null
if an asset is not available.
Links are active for 60 minutes from the API call. The API returns an error if you attempt to access assets after this period.
getReportEnc
query returns GradingReport
type representing GIA’s report data and services. This endpoint accepts encrypted report number and returns same data as getReport
endpoint.
{
getReportEnc(report_number_enrypted: "B5590075058B04A2479A66903CE536EE") {
report_date
report_number
report_type
results {
... on DiamondGradingReportResults {
__typename
shape_and_cutting_style
carat_weight
color_grade
clarity_grade
}
}
}
}
returns
{
"data": {
"getReportEnc": {
"report_date": "September 01, 2019",
"report_number": "2141438171",
"report_type": "Diamond Dossier",
"results": {
"__typename": "DiamondGradingReportResults",
"shape_and_cutting_style": "Emerald Cut",
"carat_weight": "0.51 carat",
"color_grade": "E",
"clarity_grade": "VS2"
}
}
}
}
Periodically review the status of any reports cached in your systems to avoid presenting outdated information.
You are entitled to updates on any report for 24 months from the time of the original lookup at no additional cost. This will assist you in retrieving the most recent report results without incurring any additional charges.
The getChangedReports()
query returns a paginated set of report objects that:
getChangedReports()
The clearChangedReports()
mutation clears the list of changed reports so that subsequent getChangedReports()
calls do not list the same updates.
Call
getChangedReports()
daily, so that you always have the most recent data.
Test your implementation with these sandbox reports. These reports are automatically updated on an hourly basis.
Report Number | Report Type |
---|---|
2141438172 | Diamond Dossier |
2141438169 | Diamond Grading Report |
2141438170 | Diamond Grading Report |
2201609646 | Diamond Origin Report |
6204489200 | Diamond Origin Report |
2141438174 | Colored Diamond Grading Report |
2141438175 | Colored Diamond Identification and Origin Report |
1102019845 | Identification Report |
This example will return the first two updates from the queue and a cursor. The cursor allows you to retrieve additional updates in a subsequent request.
{
getChangedReports(limit: 2) {
results {
report {
report_number
report_type
}
}
endCursor
}
}
returns
{
"data": {
"getChangedReports": {
"results": [
{
"report": {
"report_number": "10363689",
"report_type": "Colored Diamond Grading Report"
}
},
{
"report": {
"report_number": "1106785034",
"report_type": "Diamond Dossier"
}
}
],
"endCursor": "eyJ2ZXJzaW9uIjoyLCJ0b2tlbiI6IkFRSUNBSGkrT0FnPT0ifQ=="
}
}
}
The getChangedReports()
query accepts limit
and cursor
parameters. These parameters allow you to page through the results, rather than retrieving all results at once.
You have additional updates available if you received a string in endCursor
. Pass the cursor in your next query in order to obtain the additional results.
{
getChangedReports(limit: 2, cursor: "eyJ2ZXJzaW9uIjoyLCJ0b2tlbiI6IkFRSUNBSGkrT0FnPT0ifQ==") {
results {
report {
report_number
report_type
}
}
endCursor
}
}
returns
{
"data": {
"getChangedReports": {
"results": [
{
"report": {
"report_number": "1112713401",
"report_type": "Colored Diamond Grading Report"
}
},
{
"report": {
"report_number": "1112945921",
"report_type": "Diamond Dossier"
}
}
],
"endCursor": "2RVNXB2RXRWTnJ5YlROSjdlbmNDYWsyK3BML3N6dHFGR2pLVFFWK096RUlIR05"
}
}
}
Review the GraphQL Pagination Documentation for additional information.
Once you have retrieved and saved all the updates, call clearUpdatedReports()
to empty the queue.
mutation {
clearChangedReports
}
Pass a report number and date to isReportUpdated
to determine whether the report was updated after that date.
{
isReportUpdated(report_number: "6203489265", date: "2020-01-01") {
report_updated
}
}
returns
{
"data": {
"isReportUpdated": {
"report_updated": false,
}
}
}
Construct the request body and convert it to JSON before sending it to the endpoint as a POST
request with the proper headers.
Use a tool such as Insomnia to validate your query. Instructions are in the Quickstart Guide.
We’ll use this example query throughout this section.
query ReportQuery($ReportNumber: String!) {
getReport(report_number: $ReportNumber){
report_number
report_date
report_type
results {
__typename
... on DiamondGradingReportResults {
shape_and_cutting_style
carat_weight
clarity_grade
color_grade
}
}
quota {
remaining
}
}
}
Note the use of the ReportNumber
query variable.
{
"ReportNumber": "2141438171"
}
Be sure your query is successful before proceeding to the next step.
The body you send to the API must conform to GraphQL standards.
The best practice is to construct this structure using the features of your language before converting it to JSON.
The API expects this structure:
{
"query": "YOUR_QUERY_GOES_HERE",
"variables": {
"YOUR_VARIABLE_NAME_GOES_HERE": "YOUR_VARIABLE_VALUE_GOES_HERE"
}
}
You know the elements (query, variable name, and value) from Step 1. In the example above, ReportNumber
is the variable, and "2141438171"
is the value.
One way to construct the request body is to use Dictionary elements:
query_variables
of type (String
, String
).query_variables
with ReportNumber
as the index and "2141438171"
as the value.body
of type (String
, Object
)body
with query
as the index and the query you drafted in Step 1 as the value.body
with variables
as the index and query_variables
as the value// Example in C#
var query = @"
query ReportQuery($ReportNumber: String!) {
getReport(report_number: $ReportNumber){
report_number
report_date
report_type
results {
__typename
... on DiamondGradingReportResults {
shape_and_cutting_style
carat_weight
clarity_grade
color_grade
}
}
quota {
remaining
}
}
}
";
// Set the report number to lookup
var reportNumber = "2141438171";
// Construct the request body to be POSTed to the graphql server
var query_variables = new Dictionary<string, string>
{
{ "ReportNumber", reportNumber}
};
var body = new Dictionary<string, object>
{
{ "query", query },
{ "variables", query_variables }
};
Your programming language has a library for serializing objects to JSON.
// Example in C#:
string json = JsonSerializer.Serialize(body);
Tip: Avoid hand-coding your JSON body. Use the tools your language provides.
The API requires two HTTP headers:
application/json
Do not embed API keys directly in code: Embedded API keys can be accidentally exposed. Store API keys in environment variables or files outside of your application’s source tree.
// Example in C#
var client = new WebClient()
// key contains the API key
client.Headers.Add(HttpRequestHeader.Authorization, key);
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
Tip: You do not need a specialized GraphQL client to query the API. You can use the HTTP client provided by your programming language.
You are now ready to send the request to the API.
// Example in C#
// Send the payload as a JSON to the endpoint
var response = client.UploadString(url, json);
The GIA Report Results API returns responses in JSON. The shape of the JSON response aligns with the GraphQL query you submitted.
Error checking the GraphQL response requires attention at several points.
HTTP 200 OK
response.For more information, see Error Conditions.
The tools you use depend on the language you are using. With statically-typed languages such as C# and Java, you will either (a) deserialize the JSON object into a native class or (b) use a library that allows you to work with the JSON document natively.
Option (a) can be tricky given the polymorphic nature of the ReportResults
object. This approach is not discussed further in this document.
GIA has engineered this system for the utmost performance, security, and availability. This section contains details for connecting to the API reliably and securely.
A plan enables lookups in the GIA Report Results API.
Each plan has report lookups associated with it. You will no longer be able to retrieve report results when this quota runs out. Track your quota and take action when necessary.
There are multiple ways to retrieve your quota:
getQuota
to get your remaining allowance. Calling getQuota does not affect your remaining quota.getReport
and request the quota { remaining }
field as part of the response.Note: The system calculates your quota within each report lookup. If you request multiple reports in a single request, the quota remaining may be misleading. The precise quota remaining is the minimum of all
remaining
values in the response.Calling
getQuota
alone (that is, as a request separate from any calls togetReport
) will always return the exact number of report lookups remaining.
API keys authenticate and authorize requests to the API. A plan may have multiple API keys. You may revoke any key without affecting other keys on the plan.
Important: Securely store these keys and do not share them! You are responsible for taking all precautions to safeguard access to the keys.
GIA will issue you a sandbox plan and associated key as soon as your API application is submitted. This key can access a limited set of reports that represent the variety of results you can expect from the API.
See Sandbox Reports for a list of reports available under the sandbox plan.
There is no charge to add lookups to your sandbox API plan. Please contact us for assistance.
You will be able to add a production API plain once GIA approves your application. API keys associated with this plan may access the full set of reports available in GIA’s database.
Check your remaining quota by using getQuota
. Checking your quota does not affect your remaining quota.
{
getQuota{
remaining
}
}
You can also get your remaining quota with each query to getReport
.
{
getReport(report_number: "2141438171") {
report_date
report_number
report_type
quota{
remaining
}
}
}
returns
{
"data": {
"getReport": {
"report_date": "September 01, 2019",
"report_number": "2141438171",
"report_type": "Diamond Dossier",
"quota": {
"remaining": 967
}
}
}
}
Each purchase of lookups in your API plan will create a new “quota bucket”. You may query all quota buckets for the initial allocation, the lookups remaining, and the expiration date of the bucket.
{
getQuota {
remaining
buckets {
id
initial
remaining
expiration_date
status
}
}
}
returns
{
"data": {
"getQuota": {
"remaining": 1284,
"buckets": [
{
"id": "2e35ceaf-7957-4b7b-94b8-8253df37d08e",
"initial": 5000,
"remaining": 1284,
"expiration_date": "2021-02-04T15:19:00Z",
"status": "ACTIVE"
}
]
}
}
}
Rate limiting is applied based on the authorization key. You have sent too many requests if the API returns status code 429
. Check the Retry-After
header for the number of seconds to wait before resending your request.
The GIA Report Results API is engineered for high availability. You may view our current system status and historical uptime at status.gia.edu.
Important: Subscribe to notifications at status.gia.edu. These notifications are the method we will use to update you on planned maintenance or unplanned incidents.
Some fields are coded differently in this API. In most cases, we have provided both the abbreviated and full text fields.
Example:
Field | Legacy Report Check API | Report Results API |
---|---|---|
Culet | VSM | Very Small |
Girdle | STK to THK, F | Slightly Thick to Thick, Faceted |
The API returns an HTTP 200 OK
response if the report is unavailable. The errors
object includes a message and an error code.
{
"data": {
"getReport": null
},
"errors": [
{
"path": [
"getReport"
],
"data": null,
"errorType": "REPORT UNAVAILABLE",
"errorInfo": "REPORT NOT FOUND",
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "This report does not exist or is not available through Report Check. For further information, please contact us. Code: c7b893ee-63ba-4e03-be84-83ef13da222f"
}
]
}
Possible Cause | Solution |
---|---|
The requested report number does not exist. | Check your entries and try again. |
The report exists, but has not been returned to the client and is not yet available. | Retry your query after the item is returned to the client. |
The report exists but is unavailable for other reasons. | Contact GIA for further information. |
If you attempt to query a non-sandbox report with a sandbox key, you will receive an error message.
{
"data": {
"getReport": null
},
"errors": [
{
"path": [
"getReport"
],
"data": null,
"errorType": "REPORT NOT FOUND",
"errorInfo": "FORBIDDEN",
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "This API key does not have permission to access this report. If this is a sandbox API key, please obtain a production API key and retry your request."
}
]
}
Possible Cause | Solution |
---|---|
You are using a sandbox key to access a non-sandbox report number. | Obtain a production key. |
Items that are currently being serviced by GIA are unavailable through this API.
{
"data": {
"report1": null,
},
"errors": [
{
"path": [
"report1"
],
"data": null,
"errorInfo": REPORT IN HOUSE
"errorType": REPORT UNAVAILABLE
"locations": [
{
"line": 3,
"column": 3,
"sourceName": null
}
],
"message": "The item with this report number is undergoing service by GIA. Once service is completed and the item is returned, report results will be displayed. Please try your search later."
}
]
}
Possible Cause | Solution |
---|---|
The item is being serviced by GIA. | Wait until the item has been completed and returned to the client. |
An invalid key returns an HTTP 403 Forbidden
response and this body content:
{
"message": "Authorization denied."
}
Possible Cause | Solution |
---|---|
Your key is inactive. | Log in to the developer portal and confirm your key. |
Queries that exceed the plan’s quota return an HTTP 200 OK
response. Details appear in the errors
object.
{
"data": {
"getReport": null
},
"errors": [
{
"path": [
"getReport"
],
"data": null,
"errorInfo": PLAN QUOTA MET
"errorType": QUOTA REACHED
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "QuotaUsageLimit"
}
]
}
Possible Cause | Solution |
---|---|
Your quota limit has been consumed or has expired. | Add additional lookups through the developer portal. |
From time to time, GIA re-examines items and issues updated reports. In those cases, queries for the original report redirect to the current report. A note appears in the info_message
field.
Possible Cause | Solution |
---|---|
A newer report has been issued to replace the original report number. | The current report number is the most accurate data GIA has for this item. |
Asset links expire 60 minutes from the time you query the API. Requesting an asset using an expired URL returns HTTP 403 Forbidden
and a “Request has Expired” message.
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<Expires>2019-09-28T15:47:27Z</Expires>
<ServerTime>2019-09-28T16:10:51Z</ServerTime>
<RequestId>F73DF5DE0563DEE8</RequestId>
<HostId>
ADy4EQkt1sui/87CpxtQe5CMmBq8fejXckZiaB5Ui4bjfA21ky7Lp4wGBlw47A87haeruBbu90o=
</HostId>
</Error>
Possible Cause | Solution |
---|---|
The asset link has expired. | Query getReport to obtain a new asset link. |
{
"errors" : [ {
"message" : "Invalid JSON payload in POST request.",
"errorType" : "MalformedHttpRequestException"
} ]
}
Possible Cause | Solution |
---|---|
JSON is invalid. | Ensure the JSON you submit is well-formed. |
Improper nesting of escape characters. | Avoid using string concatenation and check that you are escaping properly for your language. |
The API returns an HTTP 200 OK response if the provided report number cannot be decrypted and the errors object includes an appropriate message.
{
"data": {
"getReportEnc": null
},
"errors": [
{
"path": [
"getReportEnc"
],
"data": null,
"errorType": "ARGUMENT ERROR",
"errorInfo": "report_number_enrypted invalid value",
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "A report_number_enrypted contains invalid value that cannot be decrypted properly."
}
]
}
Possible Cause | Solution |
---|---|
The encrypted report number cannot be decrypted. | Check your entries and try again. |
Your feedback drives improvements to the GIA Report Results API. Please let us know of any suggestions, ideas, or bugs that you encounter. You can find us at GIA.edu/contactus.
If you encounter an unexpected error condition in the GIA Report Results API, please include the error code returned in the response.