Dynamic QR code API

Automate your QR code creation or add QR code features to your product

requests.post( 'https://hovercode.com/', headers={'Authorization': 'Token YOUR-TOKEN'}, json=data, timeout=10 )
SCAN ME

Introduction

Hovercode’s API lets you create and update dynamic QR codes programatically. It also lets you create static codes (although they cannot be updated). It’s ideal if you need to create a lot of QR codes or if you want to add QR code generation to your product or service.

You need access to the business plan to use the API, but you can test it for free (pricing)

This API is in active development, so if there’s a missing feature or if you have any feedback, let us know and we can help.

Note: You can't make calls to this API from the browser as that would expose your API key. API calls need to be done from a back-end (PHP, Node.js, Python, Ruby etc)

Authentication

Our API uses token authentication, so with every request, you should send an authorisation token in the header like so:

Authorization: Token YOUR-TOKEN

You can find your token in your settings area while logged in. Be sure to keep your token private and secure (like you would a password) as anyone who has access to it can use the API with your credits. This is why you can't use the Hovercode API directly in the front-end

Here's what it looks like on Postman

A screenshot of the Auth tab on a Postman.com API request with

Create QR codes

https://hovercode.com/api/v2/hovercode/create/

This is the main use of the API. Use this endpoint to generate QR codes. By default, this endpoint returns the created QR code as an SVG string. You can either embed it directly into your site, or save the SVG string as an SVG file and convert it to any format you like.

If you want to QR code as a .png, set generate_png to true in your POST request body. This leads to a slower response, but the response will include a link to the QR code as a .png file as well as one as a .svg file.

Paramater name Required Description
workspace Required Every account has a workspace ID. You can find yours with your API token in your settings area
qr_data Required When using the default qr_type of "Link" this has to be a valid URL. With the type "Text" this can be any plain text
qr_type Defaults to "Link" This defaults to "Link" and can only currently be "Link or "Text". "Text" QR codes are plain text and can only be static (not dynamic)
dynamic Defaults to false Your QR code is static by default. Set this to true to make it a dynamic QR code.
display_name Not required You can optionally add a display name to your QR codes so they are easier to organise in your Hovercode dashboard (the display_name isn't customer facting)
domain Not required (defaults to the default domain from your workspace) [Only applies to dynamic QR codes, has effect on static codes] If you have multiple custom domains linked to your workspace, you can specify which you want to use here.
generate_png Not required (defaults to false) Set this to true to include a .png and .svg QR code in your response. This slows down the response. Without this set to true, the QR code is only returned as an SVG string. You can retrieve the .png or .svg file in future requests even if this has not been sert to true.
gps_tracking Not required (defaults to false) This is to enable the GPS tracking feature for the QR code. It's only for dynamic codes. More details.
error_correction Not required (defaults to 'Q' without a logo or 'H' with a logo) Use this to set the error correction of your QR code. Options are L, M, Q, or H
size Not required (defaults to 220) Sets the size of the QR code in pixels. Defaults to 220. The height is set automatically based on the width and the frame (the same if it's a square frame)
logo_url Not required Optionally add a url to an image to use it as a logo in your QR code. Don't use a massive image file and stick with pngs or jpegs
logo_round Not required When creating a QR code with a logo, you can set this to True to force the logo into a circle shape
primary_color Not required (defaults to #111111) Change the color of your QR code by adding a valid HEX color code (including the '#')
background_color Not required Change the color of your QR code background by adding a valid HEX color code (including the '#'). By default, the QR code has no background color set (it's transparent).
pattern Not required (defaults to "Original") This refers to the shape of the pattern in your QR code. The default is "Original" and the options are: Original, Circles, Squares, Diamonds, Triangles
eye_style Not required (defaults to "Square") This sets the style of the "eyes" on the three corners of the QR code. "Square" is the default and the other options are: Rounded, Drop, and Leaf
frame Not required By default your generated QR code will have no frame. The frames available through the API are the same ones you can use at hovercode.com. They are: border, border-small, border-large, square, speech-bubble, speech-bubble-above, card, card-above, text-frame, round-frame, circle-viewfinder, solid-spin, burst, scattered-lines, polkadot, and swirl
has_border Not required Some frames have a "border" option, which is false by default. If you are using a frame that has a border option you want to use, set this to true. This has no effect on QR codes using no frame or a frame with no border option.
text Not required Some frames have a "text" option, which is empty by default. If you are using a frame that has a text option you want to use, set the text here. Depending on the frame, it will have a max length. This has no effect on QR codes using no frame or a frame with no text option.

This example uses Python requests, but you can achieve something similar with Ruby, JavaScript etc. More code examples coming soon (please get in touch if you have any questions)

import requests

data = {
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "primary_color": "#1DA1F2"
}

response = requests.post(
    'https://hovercode.com/api/v2/hovercode/create/',
    headers={'Authorization': 'Token YOUR-TOKEN'},
    json=data,
    timeout=10
)
<?php

$data = array(
    "workspace" => "YOUR-WORKSPACE-ID",
    "qr_data" => "https://twitter.com/hovercodeHQ",
    "primary_color" => "#1DA1F2"
);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/create/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => json_encode($data),
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN',
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

?>
curl -X POST \
  'https://hovercode.com/api/v2/hovercode/create/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "primary_color": "#1DA1F2"
  }'
const axios = require('axios');

const data = {
    workspace: 'YOUR-WORKSPACE-ID',
    qr_data: 'https://twitter.com/hovercodeHQ',
    primary_color: '#1DA1F2'
};

axios.post('https://hovercode.com/api/v2/hovercode/create/', data, {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
require 'net/http'
require 'uri'
require 'json'

uri = URI('https://hovercode.com/api/v2/hovercode/create/')
data = {
  "workspace" => "YOUR-WORKSPACE-ID",
  "qr_data" => "https://twitter.com/hovercodeHQ",
  "primary_color" => "#1DA1F2"
}

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 10

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Token YOUR-TOKEN'
request.content_type = 'application/json'
request.body = data.to_json

response = http.request(request)
puts response.body
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newBuilder()
            .build();

        String jsonData = new StringBuilder()
            .append("{")
            .append("\"workspace\":\"YOUR-WORKSPACE-ID\",")
            .append("\"qr_data\":\"https://twitter.com/hovercodeHQ\",")
            .append("\"primary_color\":\"#1DA1F2\"")
            .append("}")
            .toString();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://hovercode.com/api/v2/hovercode/create/"))
            .timeout(Duration.ofSeconds(10))
            .header("Authorization", "Token YOUR-TOKEN")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(jsonData))
            .build();

        try {
            HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

response.json() returns the following (with the SVG string truncated in this example)

{
  {
    "id": "bc9b02bc-0d77-4828-95e7-1c27c603c5c9",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "qr_type": "Link",
    "display_name": null,
    "shortlink_url": null,
    "dynamic": false,
    "frame": null,
    "logo": null,
    "primary_color": "#1DA1F2",
    "background_color": null,
    "pattern": "Original",
    "text": null,
    "has_border": false,
    "svg":"<svg xmlns=\"http://www.w3.org/2000/svg...,
    "svg_file": null,
    "png": null,
    "created": "2023-03-30T15:02:26.343134Z"
  }
}

This is the resulting QR code:

Here's an example using more features:

import requests

data = {
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "primary_color": "#3b81f6",
    "background_color": "#FFFFFF",
    "dynamic": True,
    "display_name": "QR code for Twitter",
    "frame": "circle-viewfinder",
    "pattern": "Diamonds",
    "has_border": True,
    "logo_url": "https://hovercode.com/static/website/images/logo.png",
    "generate_png": True
}

response = requests.post(
    'https://hovercode.com/api/v2/hovercode/create/',
    headers={'Authorization': 'Token YOUR-TOKEN'},
    json=data,
    timeout=10
)
<?php

$data = array(
    "workspace" => "YOUR-WORKSPACE-ID",
    "qr_data" => "https://twitter.com/hovercodeHQ",
    "primary_color" => "#3b81f6",
    "background_color" => "#FFFFFF",
    "dynamic" => true,
    "display_name" => "QR code for Twitter",
    "frame" => "circle-viewfinder",
    "pattern" => "Diamonds",
    "has_border" => true,
    "logo_url" => "https://hovercode.com/static/website/images/logo.png",
    "generate_png" => true
);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/create/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => json_encode($data),
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN',
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X POST \
  'https://hovercode.com/api/v2/hovercode/create/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "primary_color": "#3b81f6",
    "background_color": "#FFFFFF",
    "dynamic": true,
    "display_name": "QR code for Twitter",
    "frame": "circle-viewfinder",
    "pattern": "Diamonds",
    "has_border": true,
    "logo_url": "https://hovercode.com/static/website/images/logo.png",
    "generate_png": true
  }'
const axios = require('axios');

const data = {
    workspace: 'YOUR-WORKSPACE-ID',
    qr_data: 'https://twitter.com/hovercodeHQ',
    primary_color: '#3b81f6',
    background_color: '#FFFFFF',
    dynamic: true,
    display_name: 'QR code for Twitter',
    frame: 'circle-viewfinder',
    pattern: 'Diamonds',
    has_border: true,
    logo_url: 'https://hovercode.com/static/website/images/logo.png',
    generate_png: true
};

axios.post('https://hovercode.com/api/v2/hovercode/create/', data, {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
require 'net/http'
require 'uri'
require 'json'

uri = URI('https://hovercode.com/api/v2/hovercode/create/')
data = {
  "workspace" => "YOUR-WORKSPACE-ID",
  "qr_data" => "https://twitter.com/hovercodeHQ",
  "primary_color" => "#3b81f6",
  "background_color" => "#FFFFFF",
  "dynamic" => true,
  "display_name" => "QR code for Twitter",
  "frame" => "circle-viewfinder",
  "pattern" => "Diamonds",
  "has_border" => true,
  "logo_url" => "https://hovercode.com/static/website/images/logo.png",
  "generate_png" => true
}

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 10

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Token YOUR-TOKEN'
request.content_type = 'application/json'
request.body = data.to_json

response = http.request(request)
puts response.body
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newBuilder()
            .build();

        Map<String, Object> data = Map.of(
            "workspace", "YOUR-WORKSPACE-ID",
            "qr_data", "https://twitter.com/hovercodeHQ",
            "primary_color", "#3b81f6",
            "background_color", "#FFFFFF",
            "dynamic", true,
            "display_name", "QR code for Twitter",
            "frame", "circle-viewfinder",
            "pattern", "Diamonds",
            "has_border", true,
            "logo_url", "https://hovercode.com/static/website/images/logo.png",
            "generate_png", true
        );

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://hovercode.com/api/v2/hovercode/create/"))
            .timeout(Duration.ofSeconds(10))
            .header("Authorization", "Token YOUR-TOKEN")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(data.toString()))
            .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

response.json() returns

{
    "id": "1067b811-edb3-4211-bbba-a23cac8f5ace",
    "qr_data": "https://twitter.com/hovercodeHQ",
    "qr_type": "Link",
    "display_name": "QR code for Twitter",
    "shortlink_url": "https://hov.to/4d1451cf",
    "dynamic": true,
    "frame": "circle-viewfinder",
    "logo": "https://media.hovercode.com/media/logos/0d188ceb-5992-473d-ac34-b0641ef7c030.png",
    "primary_color": "#3b81f6",
    "background_color": "#FFFFFF",
    "pattern": "Diamonds",
    "text": null,
    "has_border": true,
    "svg":"<svg xmlns=\"http://www.w3.org/2000/svg...,
    "svg_file": "https://media.hovercode.com/media/codes/1067b811-edb3-4211-bbba-a23cac8f5ace.svg",
    "png": "https://media.hovercode.com/media/codes/1067b811-edb3-4211-bbba-a23cac8f5ace.png",
    "created": "2023-03-30T16:25:40.074012Z"
  }

And here's the resulting QR code

Get QR codes

https://hovercode.com/api/v2/workspace/WORKSPACE-ID/hovercodes/

Use this endpoint to get all of the Hovercodes for your workspace. The results are paginated with 50 items per page by default.

You can search through your QR codes by adding a search query parameter q. This will search through:

  • QR code links
  • Display names
  • Shortlink URLs
  • Tag names

For example: https://hovercode.com/api/v2/workspace/WORKSPACE-ID/hovercodes/?q=twitter

import requests

response = requests.get(
'https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/hovercodes/',
headers={'Authorization': 'Token YOUR-TOKEN'},
timeout=10
)
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/hovercodes/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X GET \
  'https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/hovercodes/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/hovercodes/', {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class GetQRCodeActivity {
    public static void main(String[] args) {
        try {
            String workspaceId = "YOUR-WORKSPACE-ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/workspace/" + workspaceId + "/hovercodes/");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Authorization", "Token " + token);

            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
                StringBuilder response = new StringBuilder();
                String responseLine = null;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println(response.toString());
            }

            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'

uri = URI.parse('https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/hovercodes/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri.path)
request['Authorization'] = 'Token YOUR-TOKEN'

response = http.request(request)
puts response.read_body

The response will include a "count" - which is the total number of Hovercodes. It will also include "next" and "previous" for the next and previous pages if there are any (they will be urls).

The Hovercodes will be in "results" and each will include the following fields: 'id', 'qr_data', 'qr_type', 'display_name', 'shortlink_url', 'dynamic', 'created'

response.json() returns something like

{
  "count": 242,
  "next": "https://hovercode.com/api/v2/workspace/76741b51-dadc-42a7-b0b3-5ce23c7c7841/hovercodes/?page=2",
  "previous": null,
  "results": [
    {
      "id": "fec04f7f-16a9-4f5a-8df6-499b5eff3b9a",
      "qr_data": "http://go.co",
      "qr_type": "Link",
      "display_name": null,
      "shortlink_url": "https://tab.so/952e126c",
      "dynamic": true,
      "created": "2024-06-24T14:22:55.585649Z"
    },
    {
      "id": "8f7c1cec-f195-46cc-8540-4728680f247a",
      "qr_data": "http://go.co",
      "qr_type": "Link",
      "display_name": null,
      "shortlink_url": "https://scanqr.to/575c22c4",
      "dynamic": true,
      "created": "2024-06-21T13:01:28.194383Z"
    }...
  ]
}

Get single QR code

https://hovercode.com/api/v2/hovercode/

Use this endpoint to retrieve a QR code that was previously created. Even if generate_png wasn't set when creating the QR code, it will include the .png and .svg files of the QR code if it was created more than a few seconds before your call.

import requests

response = requests.get(
'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/',
headers={'Authorization': 'Token YOUR-TOKEN'},
timeout=10
)
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X GET \
  'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/', {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class GetQRCode {
    public static void main(String[] args) {
        try {
            String qrCodeId = "YOUR_QR_CODE_ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/hovercode/" + qrCodeId + "/");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Authorization", "Token " + token);

            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
                StringBuilder response = new StringBuilder();
                String responseLine = null;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println(response.toString());
            }

            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'

uri = URI.parse('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri.path)
request['Authorization'] = 'Token YOUR-TOKEN'

response = http.request(request)
puts response.read_body

response.json() returns

{
"id": "9c2aa461-bec0-46b8-9ed2-0edde02b164c",
"link":"https://twitter.com/hovercodeHQ",
"display_name":"None",
"square":true,
"logo":"https://media.hovercode.com/media/logos/4510095f-a9e5-48d6-870f-f81362e4bd8d.png",
"primary_color":"#1DA1F2",
"svg":"<svg class=\"m-auto\" xmlns=\"http://www.w3.org/2000/svg\...",
"svg_file":"https://media.hovercode.com/media/codes/9c2aa461-bec0-46b8-9ed2-0edde02b164c.svg",
"png":"https://media.hovercode.com/media/codes/9c2aa461-bec0-46b8-9ed2-0edde02b164c.png",
"created":"2022-09-21T06:05:40.068874Z"
}

Get QR code tracking activity

https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/

Use this endpoint to get the tracking activity data for a QR code. By default, this returns activities with 50 items per page. You can add a "page_size" paramater to the url to change the page size to anything up to 200.

import requests

response = requests.get(
'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/',
headers={'Authorization': 'Token YOUR-TOKEN'},
timeout=10
)
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X GET \
  'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/', {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class GetQRCodeActivity {
    public static void main(String[] args) {
        try {
            String qrCodeId = "YOUR_QR_CODE_ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/hovercode/" + qrCodeId + "/activity/");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Authorization", "Token " + token);

            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
                StringBuilder response = new StringBuilder();
                String responseLine = null;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println(response.toString());
            }

            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'

uri = URI.parse('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/activity/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri.path)
request['Authorization'] = 'Token YOUR-TOKEN'

response = http.request(request)
puts response.read_body

The response will include a "count" - which is the total number of items. It will also include "next" and "previous" for the next and previous pages if there are any (they will be urls).

The tracked activity results will be in "results" and each item includes: 'qr_code_id', 'time_utc', 'time_timezone_aware', 'location', 'device_string', 'scanner_id', 'id'

The 'time_timezone_aware' is based on the timezone your workspace is set to. "scanner_id" is a hash based on the scanners truncated IP and their device. It's a way to estimate whether if the QR code is being scanned by someone new or someone who has previously scanned it.

response.json() returns something like

{
  "count": 61,
  "next": "https://hovercode.com/api/v2/hovercode/[QR-CODE-ID]/activity/?page=2",
  "previous": null,
  "results": [
    {
      "qr_code_id": "[QR-CODE-ID]",
      "time_utc": "2023-03-25 05:01:31.686226+00:00",
      "time_timezone_aware": "Mar. 25, 2023, 05:01 a.m.",
      "location": "Mumbai, Maharashtra, India",
      "device": "iPhone, iOS, Mobile Safari",
      "scanner_id": "17613b5745c64cbf626766f468b9fb1ed4a3ae754c8b424d5c081fe7454fb0d5",
      "id": "eb8ee93b-8818-4354-bf8c-6d891abc07ae"
    },
    {
      "qr_code_id": "[QR-CODE-ID]",
      "time_utc": "2023-03-21 11:15:10.919115+00:00",
      "time_timezone_aware": "Mar. 21, 2023, 11:15 a.m.",
      "location": "Istanbul, Istanbul, Turkey",
      "device": "Samsung SM-F936B, Android, Samsung Internet",
      "scanner_id": "29a8c6aa8ec3f5ff179455558fa84cda397b89b919966fa865e6df683ff1078e",
      "id": "ad9de450-69df-47d0-ac9c-e6c4a33633f8"
    }...
  ]
}

Update QR codes

https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/

The power of dynamic QR codes is that you can change their scan destination after they're created. You can update the display_name or the qr_data (or both). You can also enable GPS tracking or disable GPS tracking with the gps_tracking parameter. For static QR codes, you can only update the display_name. Currently you can only update the "qr_data" for the "Link" type of dynamic QR code

import requests

data = {
    "qr_data": "https://twitter.com/ramykhuffash",
    "display_name": "hi"
}

response = requests.put(
    'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/',
    headers={'Authorization': 'Token YOUR-TOKEN'},
    json=data,
    timeout=10
)
<?php

$data = array(
    "qr_data" => "https://twitter.com/ramykhuffash",
    "display_name" => "hi"
);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_POSTFIELDS => json_encode($data),
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN',
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X PUT \
  'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "qr_data": "https://twitter.com/ramykhuffash",
    "display_name": "hi"
  }'
const axios = require('axios');

const data = {
    qr_data: 'https://twitter.com/ramykhuffash',
    display_name: 'hi'
};

axios.put('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/', data, {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class UpdateQRCode {
    public static void main(String[] args) {
        try {
            String qrCodeId = "YOUR_QR_CODE_ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/hovercode/" + qrCodeId + "/update/");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("PUT");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Authorization", "Token " + token);
            conn.setDoOutput(true);

            String jsonInputString = "{\"qr_data\": \"https://twitter.com/ramykhuffash\", \"display_name\": \"hi\"}";

            try (OutputStream os = conn.getOutputStream()) {
                byte[] input = jsonInputString.getBytes("utf-8");
                os.write(input, 0, input.length);
            }

            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
                StringBuilder response = new StringBuilder();
                String responseLine = null;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println(response.toString());
            }

            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/update/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Put.new(uri.path)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Token YOUR-TOKEN'
request.body = { "qr_data": "https://twitter.com/ramykhuffash", "display_name": "hi" }.to_json

response = http.request(request)
puts response.read_body

response.json() will return the same as you would see with the endpoint for getting a QR code.

Add tags to QR code

https://hovercode.com/api/v2/hovercode/QR-CODE-ID/tags/add/

This endpoint lets you add tags to your QR codes, which is useful for managing them and for bulk downloads. You can add multiple tag names or ids. If you add a tag name, it adds the tag if it exists, or creates then adds it if it doesn't. If you add a tag ID, it only adds it to the QR code if it exists.

import requests

data = [
    {"title": "my tag"},
    {"title": "my second tag"}
]


response = requests.post(
    'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/tags/add/',
    headers={'Authorization': 'Token YOUR-TOKEN'},
    json=data,
    timeout=10
)
<?php

$data = array(
    array("title" => "my tag"),
    array("title" => "my second tag")
);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/tags/add/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => json_encode($data),
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN',
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

?>
curl -X POST \
  'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/tags/add/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '[
    {"title": "my tag"},
    {"title": "my second tag"}
  ]'
const axios = require('axios');

const data = [
    { title: 'my tag' },
    { title: 'my second tag' }
];

axios.post('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/tags/add/', data, {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
require 'net/http'
require 'uri'
require 'json'

uri = URI('https://hovercode.com/api/v2/hovercode/YOUR-QR-CODE-ID/tags/add/')
data = [
  {"title" => "my tag"},
  {"title" => "my second tag"}
]

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 10

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Token YOUR-TOKEN'
request.content_type = 'application/json'
request.body = data.to_json

response = http.request(request)
puts response.body
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newBuilder()
            .build();

        String jsonData = "[{\"title\":\"my tag\"}, {\"title\":\"my second tag\"}]";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://hovercode.com/api/v2/hovercode/YOUR-QR-CODE-ID/tags/add/"))
            .timeout(Duration.ofSeconds(10))
            .header("Authorization", "Token YOUR-TOKEN")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(jsonData))
            .build();

        try {
            HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

response.json() will return the same as you see with the endpoint for getting a QR code.

Delete QR codes

https://hovercode.com/api/v2/hovercode/QR-CODE-ID/delete/

Here's how you delete QR codes programatically - this deletes them permanently and they will no longer scan to their intended destination.

import requests
response = requests.delete(
    'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/delete/',
    headers={'Authorization': 'Token YOUR-TOKEN'},
    timeout=10
)
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/delete/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Token YOUR-TOKEN'
    ),
));

$response = curl_exec($curl);

curl_close($curl);

echo $response;

?>
curl -X DELETE \
  'https://hovercode.com/api/v2/hovercode/QR-CODE-ID/delete/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d ''
const axios = require('axios');

axios.delete('https://hovercode.com/api/v2/hovercode/QR-CODE-ID/delete/', {
    headers: {
        Authorization: 'Token YOUR-TOKEN'
    },
    timeout: 10000
})
.then(response => {
    console.log(response.data);
})
.catch(error => {
    console.error(error);
});
require 'net/http'
require 'uri'

uri = URI('https://hovercode.com/api/v2/hovercode/YOUR-QR-CODE-ID/delete/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 10

request = Net::HTTP::Delete.new(uri)
request['Authorization'] = 'Token YOUR-TOKEN'

response = http.request(request)
puts response.body
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newBuilder()
            .build();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://hovercode.com/api/v2/hovercode/YOUR-QR-CODE-ID/delete/"))
            .timeout(Duration.ofSeconds(10))
            .header("Authorization", "Token YOUR-TOKEN")
            .DELETE()
            .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

This returns a status 204 if the delete was successful

Webhooks

If you enable Webhooks from your workspace API settings, a webhook is triggered every time a dynamic QR code from your workspace is scanned.

This feature is available on the Business Plus plan

When setting up a webhook, you need to provider a url (e.g. https://yoursite.com/hovercode/webhook)

When a webhook is triggered, there will be a POST request to that url. It will be of content-type "application/json" and will include a header called "x-signature".

Check the signature against your webhook secret token to verify the contents. If all is good, return a 200. Here's an example:


    import hmac
    import hashlib
    import json

    from django.http import JsonResponse
    

    def hc_webhook_view(request):
        webhook_secret = [YOUR WEBHOOK SECRET]
        raw_payload = request.body
        received_signature = request.headers.get('X-Signature')
        expected_signature = hmac.new(SECRET_KEY.encode(), raw_payload, hashlib.sha256).hexdigest()

        if not hmac.compare_digest(expected_signature, received_signature):
            return JsonResponse({'error': 'Invalid signature'}, status=400)
        
        payload_data = json.loads(raw_payload)
        # Do what you need to do

        return JsonResponse({'message': 'Webhook received successfully'}, status=200)
<?php

function hc_webhook_view() {
    $webhook_secret = '[YOUR WEBHOOK SECRET]';
    $raw_payload = file_get_contents('php://input');
    $received_signature = $_SERVER['HTTP_X_SIGNATURE'];
    $expected_signature = hash_hmac('sha256', $raw_payload, $webhook_secret);

    if (!hash_equals($expected_signature, $received_signature)) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid signature']);
        return;
    }

    $payload_data = json_decode($raw_payload, true);
    // Do what you need to do

    http_response_code(200);
    echo json_encode(['message' => 'Webhook received successfully']);
}

// Call the function to handle the webhook
hc_webhook_view();

?>
As this is a for receiving a webhook, there is no cURL version. Try selecting a different language
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

const SECRET_KEY = '[YOUR WEBHOOK SECRET]';

// Middleware to parse JSON request body
app.use(bodyParser.json());

app.post('/hc_webhook', (req, res) => {
    const raw_payload = JSON.stringify(req.body);
    const received_signature = req.headers['x-signature'];

    const expected_signature = crypto.createHmac('sha256', SECRET_KEY)
                                      .update(raw_payload)
                                      .digest('hex');

    if (received_signature !== expected_signature) {
        return res.status(400).json({ error: 'Invalid signature' });
    }

    // Process payload_data
    const payload_data = req.body;
    // Do what you need to do

    return res.status(200).json({ message: 'Webhook received successfully' });
});

app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.http.HttpStatus;

@RestController
public class WebhookController {

    private static final String SECRET_KEY = "YOUR_WEBHOOK_SECRET";

    @PostMapping("/hc_webhook")
    public String hcWebhookView(@RequestBody String rawPayload, @RequestHeader("X-Signature") String receivedSignature) throws IOException {
        try {
            Mac hmacSha256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
            hmacSha256.init(secretKey);
            byte[] expectedSignature = hmacSha256.doFinal(rawPayload.getBytes());
            if (!java.util.Arrays.equals(expectedSignature, receivedSignature.getBytes())) {
                return "{\"error\": \"Invalid signature\"}";
            }
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
            return "{\"error\": \"Internal server error\"}";
        }

        // Parse payload_data JSON and do what you need to do
        return "{\"message\": \"Webhook received successfully\"}";
    }
}
require 'sinatra'
require 'json'
require 'openssl'

SECRET_KEY = "YOUR_WEBHOOK_SECRET"

post '/hc_webhook' do
  request.body.rewind
  raw_payload = request.body.read
  received_signature = request.env['HTTP_X_SIGNATURE']
  hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), SECRET_KEY, raw_payload)
  if hmac != received_signature
    return { error: 'Invalid signature' }.to_json
  end

  # Parse payload_data JSON and do what you need to do
  return { message: 'Webhook received successfully' }.to_json
end

The contents will be activity data:


    {
      "qr_code_id": "2fbb014a-4b5a-4ecd-95a3-p914d4aa167b",
      "time_utc": "2023-12-13 17:44:48.920050+00:00",
      "time_timezone_aware": "Dec. 13, 2023, 05:44 p.m.",
      "location": "London, England, United Kingdom",
      "device": "iPhone, iOS, Mobile Safari",
      "scanner_id": "5dd831a872687315f54a11fa62d089e66887647c67d5ad2cd89ebd3a38084bd3",
      "id": "0acb2379-c9e3-4245-a1e3-6e542cc02637"
    }
  

This QR code API is in active development so please send us any feedback you have or let us know if there's anything we can do to help you get set up.