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
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. Get the available looking glass hosts
  2. Get the status of a measurement
  3. Search for measurements
  4. Get the results of a measurement
  5. Create a new measurement
  6. Authentication
  7. Publications
  8. Code samples

Get the available looking glass hosts

API Endpoint: http://<host.domain>/api/host/list
HTTP Method: GET

A GET request to this endpoint returns a JSON-encoded array of the available looking glass hosts with the following attributes:

  • asn [integer]: The Autonomous System Number of the host
  • router [string]: The name of the router if the same looking glass offers multiple routers, 0 otherwise
  • city [string]: The city where the looking glass router is located
  • country [string]: The country (in 2-letter ISO 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:
http://<host.domain>/api/host/list 

Response:


    [
    {
    "asn": 286,
    "router": "Amsterdam (NL)",
    "city": "Amsterdam",
    "country": "NL"
    },
    {
    "asn": 286,
    "router": "Barleben (DE)",
    "city": "Barleben",
    "country": "DE"
    },
    {
    "asn": 11403,
    "router": 0,
    "city": "New York City",
    "country": "US"
    }
    ]
  



Get the status of a measurement

API Endpoint: http://<host.domain>/api/measurement/{id}
HTTP Method: GET

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

  • id [integer]: The unique identifier of the measurement (same as {id})
  • 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. IMPORTANT: currently only traceroute is supported.
  • name [string]: The name (title/short description) of the measurement.
  • timestamp [string]: The date and time when the measurement was created.
  • queries [integer]: The number of requested queries as part of this measurement
  • status [array]: The status of the completed, active and failed queries

Example

Request:
http://<host.domain>/api/measurement/24

Response:


    {
    "id":"24",
    "argument":"198.41.209.138",
    "command":"traceroute",
    "name":"test",
    "timestamp":"2015-08-05 12:33:11",
    "queries":4,
    "status":{"completed":4,"failed":0,"active":0}
    }
  



API Endpoint: http://<host.domain>/api/measurement/find?name={format}&command={command}&argument={argument}
HTTP Method: GET
Parameters:
NameTypeValueNotes
namestring The name of the measurementOPTIONAL
commandstring traceroute | traceroute6 | ping | ping6 | bgp | bgp6OPTIONAL
argumentstring The target of the measurementOPTIONAL

To search for specific measurements send a GET request to http://<host.domain>/api/measurement/find
All parameters are optional but at least one argument is required.

The response is a JSON array of measurement status objects.

Example

Request:
http://<host.domain>/api/measurement/find?name=test

Response:


      [
      {
      "id": "24",
      "argument": "198.41.209.138",
      "command": "traceroute",
      "name": "test",
      "timestamp": "2015-08-05 12:33:11",
      "queries": 4,
      "status": {
      "completed": 4,
      "failed": 0,
      "active": 0
      }
      },
      {
      "id": "44",
      "argument": "173.194.192.102",
      "command": "traceroute",
      "name": "test",
      "timestamp": "2015-08-06 22:45:44",
      "queries": 6,
      "status": {
      "completed": 5,
      "failed": 1,
      "active": 0
      }
      },
      {
      "id": "46",
      "argument": "23.7.125.239",
      "command": "traceroute",
      "name": "test",
      "timestamp": "2015-08-06 23:14:43",
      "queries": 6,
      "status": {
      "completed": 6,
      "failed": 0,
      "active": 0
      }
      }
      ]
    



Get the results of a measurement

API Endpoint: http://<host.domain>/api/measurement/{id}/result?format={format}
HTTP Method: GET
Parameters:
NameTypeValueNotes
formatstring json | iplane | rawOPTIONAL

A GET request to this endpoint returns a JSON-encoded array with the status AND the results of the measurement with id {id}. On top of the measurement status attributes the response includes the queries attribute which includes the following sub-attributes:

  • id [integer]: The unique identifier of the looking glass query.
  • asn [integer]: The ASN of the looking glass host.
  • router [string]: 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 2-letter ISO format) where the looking glass router is located
  • status [string]: The status of the query (completed or failed)
  • result [array]: The result of the query.

The default format for the result is JSON with similar schema to the one used by RIPE Atlas. You can use the optional parameter format to change the format of the query result. The result parameter can take three possible values:

  1. json: the default format
  2. iplane: the iplane text format
  3. raw: the result format of the looking glass. Looking glasses uses different ouput format, therefore the raw format will may differ between looking glasses. For this reason it is advised to use the json or the iplane formats.

Example

Request:
  http://<host.domain>/api/measurement/24/result

Response:


      {
      "id": "24",
      "argument": "198.41.209.138",
      "command": "traceroute",
      "name": "test",
      "timestamp": "2015-08-05 12:33:11",
      "queries": [
      {
      "id": 38,
      "asn": "16097",
      "router": "0",
      "city": "Frankfurt",
      "country": "DE",
      "status": "completed",
      "result": [
      {
      "asn": "16097",
      "dst_addr": "198.41.209.138",
      "result": [
      {
      "hop": 1,
      "result": [
      {
      "from": "85.232.3.217",
      "rtt": "1"
      }
      ]
      },
      {
      "hop": 2,
      "result": [
      {
      "from": "62.69.146.42",
      "rtt": "1"
      }
      ]
      },
      {
      "hop": 3,
      "result": [
      {
      "from": "198.41.209.138",
      "rtt": "1"
      }
      ]
      }
      ],
      "router": "FMA2.hlkomm.net",
      "timestamp": 1438818228.0901
      }
      ]
      },
      {
      "id": 3,
      "asn": "2828",
      "router": "Dallas-TX",
      "city": "Dallas",
      "country": "US",
      "status": "completed",
      "result": [
      {
      "asn": "2828",
      "dst_addr": "198.41.209.138",
      "result": [
      {
      "hop": 1,
      "result": [
      {
      "from": "64.35.126.41",
      "rtt": "0.076"
      }
      ]
      },
      {
      "hop": 2,
      "result": [
      {
      "from": "216.156.0.81",
      "rtt": "1.985"
      }
      ]
      },
      {
      "hop": 3,
      "result": [
      {
      "from": "207.88.14.241",
      "rtt": "0.512"
      }
      ]
      },
      {
      "hop": 4,
      "result": [
      {
      "from": "213.248.81.237",
      "rtt": "0.566"
      }
      ]
      },
      {
      "hop": 5,
      "result": [
      {
      "from": "62.115.44.2",
      "rtt": "0.593"
      }
      ]
      },
      {
      "hop": 6,
      "result": [
      {
      "from": "198.41.209.138",
      "rtt": "0.517"
      }
      ]
      }
      ],
      "router": "Dallas-TX",
      "timestamp": 1438818592.8654
      }
      ]
      }
      ],
      "status": {
      "completed": 4,
      "failed": 0,
      "active": 0
      }
      }
    

Example

Request:
  http://<host.domain>/api/measurement/24/result?format=iplane

Response:


      {
      "id": "41",
      "argument": "198.41.209.138",
      "command": "traceroute",
      "name": "test",
      "timestamp": "2015-08-06 04:59:53",
      "queries": [
      {
      "id": 81,
      "asn": "286",
      "router": "Amsterdam (NL)",
      "city": "Amsterdam",
      "country": "NL",
      "status": "completed",
      "result": "traceroute 198.41.209.138\n1 134.222.48.245 134.222.48.245 0.829\n2 62.115.35.197 62.115.35.197 0.866\n3 62.115.140.162 62.115.140.162 1.380\n4 213.155.137.197 213.155.137.197 3.688\n5 62.115.36.94 62.115.36.94 1.453\n6 198.41.209.138 198.41.209.138 1.184\n"
      },
      {
      "id": 82,
      "asn": "8218",
      "router": "tcr1.eun.ams",
      "city": "Amsterdam",
      "country": "NL",
      "status": "completed",
      "result": "traceroute 198.41.209.138\n1 193.239.117.114 193.239.117.114 8.742\n2 198.41.209.138 198.41.209.138 0.594\n"
      },
      {
      "id": 83,
      "asn": "56851",
      "router": "16",
      "city": "Novosibirsk",
      "country": "RU",
      "status": "completed",
      "result": "traceroute 198.41.209.138\n1 95.167.71.22 95.167.71.22 0.029\n2 87.226.133.245 87.226.133.245 95.518\n3 80.249.211.140 80.249.211.140 95.323\n4 198.41.209.138 198.41.209.138 100.662\n"
      }
      ],
      "status": {
      "completed": 3,
      "failed": 0,
      "active": 0
      }
      }
    



Create a new measurement

API Endpoint: http://<host.domain>/api/measurement/
HTTP Method: POST
Parameters:
NameTypeValueNotes
argumentstring The IP address to probeCurrently only IP addresses are supported, no hostnames
commandstring traceroute | traceroute6 | ping | ping6 | bgp | bgp6Currently only traceroute is supported.
namestring A short description for the measurementMultiple measurements can have the same name
hostsarray The looking glass hosts that will be used to execute the command targeting the argument>Each hosts element is an array with the following attributes: ASN, router

To create a new measurement you need to send a POST request to http://<host.domain>/api/measurement/ with the payload encoded as JSON array. If the rquest is accepted the API returns the ID of the measurement that you can later use to query the status and the results of the measurement.

Example

Example request for a new measurement. This measurement will attempt to execute a traceroute to 198.41.209.138 from 6 different looking glass hosts belonging in 5 different ASes.

Request:

    {
    "argument": "198.41.209.138",
    "command": "traceroute",
    "name": "test",
    "hosts": [
    {
    "asn": "286",
    "router": "109.87.188.254"
    },
    {
    "asn": "8218",
    "router": "tcr1.eun.ams"
    },
    {
    "asn": "56851",
    "router": "16"
    },
    {
    "asn": "36351",
    "router": "31"
    },
    {
    "asn": "12989",
    "router": "R1.AM (Amsterdam NL)"
    },
    {
    "asn": "12989",
    "router": "R1.BR (Brussels BE)"
    }
    ]
    }
  

Response:


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


Authentication

All the data collected through the Periscope API are public and therefore API requests that query the results of measurements (GET requests) do not require authentication.
Users need to be authenticated in order to create measurements, i.e. to issue POST requests to the API.

Periscope uses HMAC SHA-256 (Hash Message Authentication Code) for authentication. Each user is assigend a public and a private key. The private key is used to calculate the HMAC string of request which will then be sent to the API along with the public key to sign the request. If the request is failed to be authenticated the API returns a 401 HTTP error.

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.



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 similar to the previous example:


    import requests
    import json
    # Libraries required to calculate the HMAC signature
    import hmac, hashlib, base64

    api_url = "http://<host.domain>/api/measurement"
    # Construct the measurement array
    measurement = dict()
    measurement["argument"] = "23.7.125.239"
    measurement["command"]= "traceroute"
    measurement["name"] = "test"
    measurement["hosts"] = [
    {"asn": "286", "router": "Amsterdam (NL)"},
    {"asn": "8218", "router": "tcr1.eun.ams"},
    {"asn": "56851", "router": "16"},
    {"asn": "36351", "router": "31"},
    {"asn": "12989", "router": "R1.AM (Amsterdam NL)"},
    {"asn": "12989", "router": "R1.BR (Brussels BE)"}
    ]

    # When encoding the data array to JSON string it's important to make sure that the JSON string will not have empty spaces after the separators.
    data = json.dumps(measurement, sort_keys=True, separators=(',', ':'))

    # Calculate the HMAC signature
    public_key = "my_public_key"
    secret_key = "my_private_key"
    signature = base64.b64encode(hmac.new(secret_key, msg=data, digestmod=hashlib.sha256).digest())
    sanitized_sig = signature.replace('+','-').replace('=','_').replace('/','~')
    # Set the authentication headers
    headers = {'X-Public': public_key, 'X-Hash': sanitized_sig}

    # Post the data and the authentication headers
    response = requests.post(api_url, data=data, headers=headers)
    # Convert the response from JSON string to array
    decoded_response = response.json()
    print "Measurement ID is " + decoded_response["id"]
  

The same example in PHP:


    // construct the request array
    $measurement = array(
    "argument"=>"173.194.192.102",
    "command"=>"traceroute",
    "name" => "test",
    "hosts"=>array(
    array("asn"=>"286", "router"=>"Amsterdam (NL)"),
    array("asn"=>"8218", "router"=>"tcr1.eun.ams"),
    array("asn"=>"56851", "router"=>"16"),
    array("asn"=>"36351", "router"=>"31"),
    array("asn"=>"12989", "router"=>"R1.AM (Amsterdam NL)"),
    array("asn"=>"12989", "router"=>"R1.BR (Brussels BE)")
    )
    );
    // Encode the request as JSON string
    $data = json_encode($measurement);

    // Calculate the HMAC signature
    $private_key = 'my_public_key';
    $public_key = 'my_secret_key';
    $sig = base64_encode(hash_hmac('sha256', $data, $private_key, TRUE));
    $sig = str_replace(array('+', '=', '/'), array('-', '_', '~'), $sig);

    // Set the authentication headers
    $headers = array(
    'X-Public:' . $public_key,
    'X-Hash:' . $sig
    );

    $ch = curl_init('http://<host.domain>/api/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);
    // Convert the response from JSON string to array
    $decoded_response = json_decode($response,1);
    print "Measurement ID is ".$decoded_response["id"];
  

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 212.58.246.104


    import requests
    import json
    import random


    api_url = "http://<host.domain>/api"
    # 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 array
    measurement = dict()
    measurement["argument"] = "212.58.246.104"
    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: Mon Jul-10-2017 14:28:31 PDT
  Page URL: http://www.caida.org/tools/utilities/looking-glass-api/index.xml