Skip to Content
[CAIDA - Center for Applied Internet Data Analysis logo]
Center for Applied Internet Data Analysis
www.caida.org > tools : utilities : looking-glass-api
Periscope Looking Glass API
The Periscope service is currently down. New accounts are on hold. Please click the "Request Account" link below and fill out the form to indicate interest in an account once we return the service to production. CAIDA's Periscope Looking Glass API is a middleware interface to looking glass server nodes, with with options to retreive measurements status, search by type of measurement, and create new measurements.
|   Request Account    Publications   |

Download

The Periscope Looking Glass API is available for limited download only request: Request a Periscope account.


Documentation

Contents

  1. General
  2. Get the available looking glass hosts
  3. Get the status of a measurement
  4. Search for measurements
  5. Get the results of a measurement
  6. Create a new measurement
  7. Publications
  8. Code samples

General

All requests to the Periscope API must be made via HTTP[S] GET and POST requests. All responses to API requests are in JSON format. Responses to successful requests will include an HTTP response code in the range 200-299. Responses to failed requests will include an HTTP response code outside that range, and a body that is a JSON object with the attribute "errors" containing a list of human-readable error messages.

All measurement data collected by Periscope are public and therefore API requests that query the results of measurements (GET requests) do not require authentication, and may be made by HTTP or HTTPS.

Requests to create measurements require a POST request over HTTPS (not HTTP), and authentication with HMAC SHA-256 (Hash Message Authentication Code). Each authorized user is issued a public key and a private key. The private key is used to calculate the HMAC string of the request body. If the request authenticaion fails, the API returns a HTTP 401 error. POST requests must contain the following HTTP headers:

  • Content-type: "application/json; charset=utf-8" (other charset values will also work; just make sure your content matches. Non-ASCII characters occur in Periscope data only in city names, and then only rarely.)
  • X-Public: public key
  • X-Hash: base64-encoded HMAC-SHA-256 signature of request body

The Code samples section provides Python and PHP examples on how to perform the authentication.

To obtain a public and a private key please Request a Periscope account.



Get the available looking glass hosts

API Endpoint: https://periscope.caida.org/api/v2/host/list
HTTP Method: GET

A GET request to this endpoint returns a JSON-encoded array of objects describing the available looking glass hosts. The host objects have the following attributes:

  • asn [integer]: The Autonomous System Number of the host
  • router [string or integer]: The name of the router if the looking glass offers one or more named routers, 0 otherwise
  • city [string]: The city where the looking glass router is located
  • country [string]: The country (in ISO-3166-1 alpha-2 format) where the looking glass router is located

When the looking glass provides the city and the country of the router we use this information, otherwise we infer the router location based on geographical DNS hints in the first hop, or the NetAcuity geolocation database. Therefore the geolocation may not always be accurate.

Example

Request:
https://periscope.caida.org/api/v2/host/list

Response:


    [
	{
	    "asn": 64496,
	    "router": "Amsterdam (NL)",
	    "city": "Amsterdam",
	    "country": "NL"
	},
	{
	    "asn": 64496,
	    "router": "Frankfurt (DE)",
	    "city": "Frankfurt",
	    "country": "DE"
	},
	{
	    "asn": 65551,
	    "router": 0,
	    "city": "New York City",
	    "country": "US"
	}
    ]
  



Get the status of a measurement

API Endpoint: https://periscope.caida.org/api/v2/measurement/id
HTTP Method: GET

A GET request to this endpoint returns a JSON-encoded object with the status of the measurement with id id, with the following attributes:

  • id [integer]: The unique identifier of the measurement (same as the id parameter)
  • argument [string]: The IP address used as target in this measurement's looking glass queries
  • command [string]: The looking glass command used in this measurement's queries. Currently only traceroute is supported.
  • name [string]: The name of the measurement specified when the measurement was created.
  • timestamp [string]: The date and time when the measurement was created.
  • queries [integer]: The number of queries requested as part of this measurement
  • status: object with the following attributes:
    • completed [integer]: number of completed queries
    • active [integer]: number of queries still in progress
    • failed [integer]: number of failed queries

Example

Request:
https://periscope.caida.org/api/v2/measurement/1250021

Response:


    {
	"id":"1250021",
	"argument":"198.51.100.7",
	"command":"traceroute",
	"name":"test",
	"timestamp":"2018-12-05 12:33:11",
	"queries":4,
	"status":{"completed":4, "failed":0, "active":0}
    }
  



API Endpoint: https://periscope.caida.org/api/v2/measurement/find?name=name&command=command&argument=argument
HTTP Method: GET
Parameters:
NameTypeValueNotes
namestringThe name of the measurementOPTIONAL
commandstring"traceroute" OPTIONAL
argumentstringThe target of the measurementOPTIONAL

To search for specific measurements send a GET request to https://periscope.caida.org/api/v2/measurement/find
All parameters are optional but at least one is required.

The response is a JSON array of measurement status objects.

Example

Request:
https://periscope.caida.org/api/v2/measurement/find?name=test

Response:


      [
	{
	  "id":"1250021",
	  "argument":"198.51.100.7",
	  "command":"traceroute",
	  "name":"test",
	  "timestamp":"2018-12-05 12:33:11",
	  "queries":4,
	  "status":{"completed":4, "failed":0, "active":0}
	},
	{
	  "id":"1250022",
	  "argument":"203.0.113.22",
	  "command":"traceroute",
	  "name":"test",
	  "timestamp":"2018-12-05 13:21:47",
	  "queries":5,
	  "status":{"completed":5, "failed":1, "active":0}
	},
	{
	  "id":"1250023",
	  "argument":"192.0.2.250",
	  "command":"traceroute",
	  "name":"test",
	  "timestamp":"2018-12-05 13:37:29",
	  "queries":8,
	  "status":{"completed":8, "failed":0, "active":0}
	},
      ]
    



Get the results of a measurement

API Endpoint: https://periscope.caida.org/api/v2/measurement/id/result?format=format
HTTP Method: GET
Parameters:
NameTypeValueNotes
formatstring"json" | "iplane" | "raw"OPTIONAL, default "json"

A GET request to this endpoint returns a JSON-encoded object with the status and results of the measurement with id id. The object has all the same attributes as a status object, plus a "queries" attribute:

  • id [integer]: The unique identifier of the measurement (same as the id parameter)
  • argument [string]: The IP address used as target in this measurement's looking glass queries
  • command [string]: The looking glass command used in this measurement's queries. Currently only traceroute is supported.
  • name [string]: The name of the measurement specified when the measurement was created.
  • timestamp [string]: The date and time when the measurement was created.
  • queries [integer]: The number of queries requested as part of this measurement
  • status: object with the following attributes:
    • completed [integer]: number of completed queries
    • active [integer]: number of still-active queries
    • failed [integer]: number of failed queries
  • queries: array of objects with the following attributes:
    • id [integer]: The unique identifier of the looking glass query.
    • asn [integer]: The ASN of the looking glass host.
    • router [string or integer]: The looking glass router used to run the query (0 if this looking glass has only one router).
    • city [string]: The city where the looking glass router is located
    • country [string]: The country (in ISO-3166-1 alpha-2 format) where the looking glass router is located
    • status [string]: The status of the query ("completed", "failed", or "active")
    • result [optional]: If status is "completed", this contains the result of the query, formatted according to the format parameter.
    • raw_result [optional string]: If status is "failed", this field may contain an incomplete result string that may be useful for diagnosing the failure.
    • error [optional string]: If status is "failed", this will contain an error message.

The format of the result attribute in a completed query depends on the format parameter:

  • "json": a JSON-encoded object (similar to that used by RIPE Atlas) with the following attributes:
    • asn [integer]: Autonomous System Number of the looking glass host
    • dst_addr [string]: destination address of the traceroute
    • endtime [integer]: unix timestamp of the end of the measurement
    • result: array of objects with the following attributes:
      • hop [integer]: traceroute hop number
      • result: array of objects (usually 3) describing the response to a probe packet. If there was a response, it will contain the following attributes:
        • from [string]: IP address of response
        • rtt [number]: round trip time of response
        If there was no response, the object will contain:
        • x: "*"
    • router [string or integer]: The looking glass router used to run the query, or 0 if this looking glass has only one unnamed router.
    • size [optional integer]: packet size in bytes, if known
    • timestamp [integer]: unix timestamp of the start of the measurement
    • type: "traceroute"
  • "iplane": a string containing the iplane text format
  • "raw": a string containing the original text returned by the looking glass server. Because raw format differs between looking glasses, you are advised to use the "json" or "iplane" formats instead.

Example

Request:
  https://periscope.caida.org/api/v2/measurement/1250062/result

Response:


{
  "argument": "192.51.100.7", 
  "command": "traceroute", 
  "id": 1250062, 
  "name": "example1", 
  "queries": [
    {
      "asn": 64496, 
      "city": "New York", 
      "country": "US", 
      "id": 5516730, 
      "result": {
        "asn": 64496, 
        "dst_addr": "198.51.100.7", 
        "endtime": 1543539422, 
        "result": [
          {
            "hop": 1, 
            "result": [
              { "from": "203.0.113.97", "rtt": 0.592 }, 
              { "from": "203.0.113.97", "rtt": 0.581 }, 
              { "from": "203.0.113.97", "rtt": 0.575 }
            ]
          }, 
          {
            "hop": 2, 
            "result": [
              { "from": "192.0.2.42", "rtt": 0.214 }, 
              { "from": "192.0.2.42", "rtt": 0.212 }, 
              { "from": "192.0.2.42", "rtt": 0.206 }
            ]
          }, 
          {
            "hop": 3, 
            "result": [
              { "from": "10.255.255.9", "rtt": 0.202 }, 
              { "from": "10.255.255.9", "rtt": 0.272 }, 
              { "from": "10.255.255.9", "rtt": 0.188 }
            ]
          }, 
          {
            "hop": 4, 
            "result": [
              { "from": "198.51.100.7", "rtt": 0.34 }, 
              { "from": "198.51.100.7", "rtt": 0.331 }, 
              { "from": "198.51.100.7", "rtt": 0.332 }
            ]
          }
        ], 
        "router": "r1-newyork", 
        "size": 60, 
        "timestamp": 1543539392, 
        "type": "traceroute"
      }, 
      "router": "r1-newyork", 
      "status": "completed"
    }
  ], 
  "status": { "active": 0, "completed": 1, "failed": 0 }, 
  "timestamp": "2018-12-10 22:57:07"
}
    

Example

Request:
  https://periscope.caida.org/api/v2/measurement/1250062/result?format=raw

Response:


{
  "argument": "192.51.100.7", 
  "command": "traceroute", 
  "id": 1250062, 
  "name": "example1", 
  "queries": [
    {
      "asn": 64496, 
      "city": "New York", 
      "country": "US", 
      "id": 5516730, 
      "result": "traceroute to 198.51.100.7 (198.51.100.7), 30 hops max, 60 byte packets\n 1  abcdefg.example.org (203.0.113.97)  0.592 ms  0.581 ms  0.575 ms\n 2  wxyz.example.com (192.0.2.42)  0.214 ms  0.212 ms  0.206 ms\n 3  10.255.255.19 (10.255.255.9)  0.202 ms  0.272 ms  0.188 ms\n 4  target.example.net (198.51.100.7)  0.340 ms  0.331 ms  0.332 ms\n", 
      "router": "r1-newyork", 
      "status": "completed"
    }
  ], 
  "status": { "active": 0, "completed": 1, "failed": 0 }, 
  "timestamp": "2018-12-10 22:57:07"
}
    

Example

Request:
  https://periscope.caida.org/api/v2/measurement/1250062/result?format=iplane

Response:


{
  "argument": "192.51.100.7", 
  "command": "traceroute", 
  "id": 1250062, 
  "name": "example1", 
  "queries": [
    {
      "asn": 64496, 
      "city": "New York", 
      "country": "US", 
      "id": 5516730, 
      "result": "traceroute 198.51.100.7\n1 203.0.113.97 203.0.113.97 0.592\n2 192.0.2.42 192.0.2.42 0.214\n3 10.255.255.9 10.255.255.9 0.202\n4 198.51.100.7 198.51.100.7 0.34\n", 
      "router": "r1-newyork", 
      "status": "completed"
    }
  ], 
  "status": { "active": 0, "completed": 1, "failed": 0 }, 
  "timestamp": "2018-12-10 22:57:07"
}
    



Create a new measurement

API Endpoint: https://periscope.caida.org/api/v2/measurement/
HTTP Method: POST
Parameters:

To create a new measurement, send a POST request to https://periscope.caida.org/api/v2/measurement/ with the payload encoded as JSON dictionary with the following attributes:

  • argument [string]: The IP address to probe (not hostname)
  • command [string]: "traceroute"
  • name [string]: A user-chosen name for the measurement that can be used to find it later. Multiple measurements can have the same name.
  • hosts A JSON-encoded array of objects with the following attributes, identifying the looking glass hosts that will be used to execute command with argument:
    • asn [integer]: The Autonomous System Number of the host
    • router [string or integer]: The name of the host, or the 0-based index of the host within the AS (all supported ASes have at least one router with index 0).

If the request is accepted, the API returns an HTTP status of 201 (Created) and a payload with a JSON-encoded object with the following attributes:

  • message [string]: "Measurement created"
  • id [integer]: a unique identifier for the newly created measurement

Example

Example request for a new measurement that will attempt to execute a traceroute to 192.51.100.7 from 5 different looking glass hosts belonging to 3 different ASes.

Request body:

  {
    "command":"traceroute",
    "argument":"192.51.100.7",
    "name":"example1",
    "hosts":[
	{"asn":64496, "router":"r1-newyork" },
	{"asn":64496, "router":"r2-paris" },
	{"asn":64496, "router":"r3-london" },
	{"asn":65537, "router":"lax001.example.net" },
	{"asn":65550, "router":0 },
    ]
  }
  

Response:


  {"message":"Measurement created", "id":"1250021"}
  


Publications

Please cite the Periscope paper if you use the Periscope Looking Glass API in a publcation:
  V. Giotsas, A. Dhamdhere, k. claffy.
  "Periscope: Unifying Looking Glass Querying".
  In proceedings of the 2016 Passive and Active Measurements Conference (PAM'16)
  31 March - 1 April 2016, Heraklion, Greece

Also, please email us your publication at data-info AT caida DOT org to add it in this page

Code Samples

Sample 1

The following Python script creates a measurement like that in the previous example:


# python character encoding: utf-8
import sys
import requests
import json
import hmac, hashlib, base64 # for calculating the HMAC signature

api_url = "https://periscope.caida.org/api/v2"
# Construct the measurement request
measurement = dict()
measurement["command"] = "traceroute"
measurement["argument"] = "192.51.100.7"
measurement["name"] = "example1"
measurement["hosts"] = [
    {"asn": 64496, "router": "r1-newyork" },
    {"asn": 64496, "router": "r2-paris" },
    {"asn": 64496, "router": "r3-london" },
    {"asn": 65537, "router": "lax001.example.net" },
    {"asn": 65550, "router": 0 },
]

data = json.dumps(measurement)

# Calculate the HMAC signature
public_key = "my_public_key"
private_key = "my_private_key"
signature = base64.b64encode(hmac.new(private_key, msg=data,
    digestmod=hashlib.sha256).digest())

# Set the headers
headers = {
    'Content-type': 'application/json; charset=utf-8',
    'X-Public': public_key,
    'X-Hash': signature
}

print "Request headers:  " + repr(headers)
print "Request data:  " + data

# Post the data and headers
response = requests.post(api_url + "/measurement", data=data, headers=headers)

print "HTTP response status: " + str(response.status_code)
print "HTTP response headers: " + str(response.headers)
print "HTTP response text:  " + str(response.text)

if response.status_code == 201:
    decoded_response = response.json()
    print "Measurement ID: " + str(decoded_response["id"])
else:
    sys.exit(1)
  

The same example in PHP:


<?php
$api_url = "https://periscope.caida.org/api/v2";

// Construct the measurement request
$measurement = [
    "command" => "traceroute",
    "argument" => "192.51.100.7",
    "name" => "example1",
    "hosts" => [
        ["asn" => 64496, "router" => "r1-newyork" ],
        ["asn" => 64496, "router" => "r2-paris" ],
        ["asn" => 64496, "router" => "r3-london" ],
        ["asn" => 65537, "router" => "lax001.example.net" ],
        ["asn" => 65550, "router" => 0 ],
    ]
];
$data = json_encode($measurement);

# Calculate the HMAC signature
$public_key = 'my_public_key';
$private_key = 'my_private_key';
$signature = base64_encode(hash_hmac('sha256', $data, $private_key, TRUE));

# Set the headers
$headers = [
    'Content-type:application/json; charset=utf-8',
    'X-Public:' . $public_key,
    'X-Hash:' . $signature
];

print "Request headers:";
print_r($headers);
print "Request data:  " . $data . "\n";

# Post the data and headers
$ch = curl_init($api_url . "/measurement");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
$response_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

print "HTTP response status: " . curl_getinfo($ch, CURLINFO_RESPONSE_CODE) . "\n";
print "HTTP response text:  " . $response . "\n";

if ($response_code == 201) {
    $decoded_response = json_decode($response, 1);
    print "Measurement ID: " . $decoded_response["id"] . "\n";
} else {
    exit(1);
}
  

Sample 2

The following Python script gets the list of the available looking glass nodes, selects 30 nodes randomly and creates a new traceroute measurement to 192.51.100.7:


    import requests
    import json
    import random


    api_url = "https://periscope.caida.org/api/v2"
    # Get the available looking glass nodes
    response = requests.get(api_url + "/host/list")
    available_hosts = response.json()
    print "Number of available hosts: "+str(len(available_hosts))
    # Select 20 random looking glass hosts for the measurement
    hosts = random.sample(available_hosts, 30)

    # Construct the measurement request
    measurement = dict()
    measurement["argument"] = "192.51.100.7"
    measurement["command"]= "traceroute"
    measurement["name"] = "test"
    measurement["hosts"] = list()
    for host in hosts:
    measurement["hosts"].append({"asn": host["asn"], "router": host["router"]})

    print json.dumps(measurement)

    # Send the data encoded as JSON string
    response = requests.post(api_url + "/measurement",
	data=json.dumps(measurement))
    # Convert the response from JSON string to array
    decoded_response = response.json()
    print "Measurement ID is " + decoded_response["id"]
  
  Last Modified: Wed Feb-6-2019 15:53:01 PST
  Page URL: http://www.caida.org/tools/utilities/looking-glass-api/index.xml