Dynamic QR code API

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

View as Markdown
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, short links, and GS1 Digital Link codes programmatically, and read your forms and their submissions. It's ideal for creating QR codes in bulk or adding QR/link features to your own product.

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

Note: calls must be made from a back-end — never the browser — so your token isn't exposed.

Base URL

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

AUTH Authentication

The API uses token authentication. Send your token in the Authorization header on every request:

Authorization: Token YOUR-TOKEN

You can find your token in your settings area while logged in. Keep it secret — anyone with it can use your credits.

QR codes

Generate styled QR codes. The same create endpoint produces every QR type via the qr_type field.

POST Create a QR code

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

Returns the QR code as an SVG string by default. Set generate_png to true to also get .png and .svg file URLs (slower). qr_type defaults to "Link"; use "Text" for plain text, or see the vCard and GS1 sections for those types.

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.
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();
        }
    }
}

This is the resulting QR code:

POST Create a vCard QR code

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

Set qr_type to vCard and pass a nested vcard object (instead of qr_data). A dynamic vCard (set dynamic to true) is editable later; a static one (the default) encodes the contact directly. first_name is required; all other vCard fields are optional. (Profile photos aren't supported via the API yet.)

import requests

data = {
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_type": "vCard",
    "dynamic": True,
    "vcard": {
        "first_name": "Ada",
        "last_name": "Lovelace",
        "company_name": "Analytical Engines",
        "email": "ada@example.com",
        "mobile_number": "+1 555 0100"
    }
}

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_type" => "vCard",
    "dynamic" => true,
    "vcard" => array(
        "first_name" => "Ada",
        "last_name" => "Lovelace",
        "company_name" => "Analytical Engines",
        "email" => "ada@example.com",
        "mobile_number" => "+1 555 0100"
    )
);

$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_type": "vCard",
    "dynamic": true,
    "vcard": {
      "first_name": "Ada",
      "last_name": "Lovelace",
      "company_name": "Analytical Engines",
      "email": "ada@example.com",
      "mobile_number": "+1 555 0100"
    }
  }'
const axios = require('axios');

const data = {
    workspace: 'YOUR-WORKSPACE-ID',
    qr_type: 'vCard',
    dynamic: true,
    vcard: {
        first_name: 'Ada',
        last_name: 'Lovelace',
        company_name: 'Analytical Engines',
        email: 'ada@example.com',
        mobile_number: '+1 555 0100'
    }
};

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_type" => "vCard",
  "dynamic" => true,
  "vcard" => {
    "first_name" => "Ada",
    "last_name" => "Lovelace",
    "company_name" => "Analytical Engines",
    "email" => "ada@example.com",
    "mobile_number" => "+1 555 0100"
  }
}

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_type\":\"vCard\",")
            .append("\"dynamic\":true,")
            .append("\"vcard\":{")
            .append("\"first_name\":\"Ada\",")
            .append("\"last_name\":\"Lovelace\",")
            .append("\"company_name\":\"Analytical Engines\",")
            .append("\"email\":\"ada@example.com\",")
            .append("\"mobile_number\":\"+1 555 0100\"")
            .append("}")
            .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();
        }
    }
}

The response echoes the contact back under a vcard key, with the usual QR fields.

GET Get QR codes

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

Returns your workspace's QR codes, paginated 50 per page. Add ?q= to search links, display names, shortlink URLs and tags.

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

GET Get a single QR code

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

Retrieve a previously created QR code, including its .png and .svg file URLs once they've been generated.

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

GET Get QR code activity

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

Tracking activity for a QR code, paginated (up to 200 per page via page_size).

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

PATCH Update a QR code

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

Change the display_name or, for dynamic codes, the qr_data (scan destination). Also toggles gps_tracking.

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

POST Add tags to a QR code

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

Add tag names or IDs to a QR code. New tag names are created if they don't exist.

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();
        }
    }
}

DELETE Delete a QR code

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

Permanently deletes the QR code. Returns 204 on success.

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();
        }
    }
}

Short links

Short links are the same primitive that powers dynamic QR codes, without the visual. A short link has a QR code too — its SVG is generated immediately, while the .png is only created when you request the QR image, keeping creation fast.

Forms

Forms collect submissions through a shareable link or QR code. These endpoints are read-only — build and edit forms in the dashboard, then read your forms and their submissions programmatically. Both are paginated; add ?page= and ?page_size= (max 100) to page through results.

GET List forms

https://hovercode.com/api/v2/forms/?workspace=WORKSPACE-ID

A workspace's forms, newest first. ?workspace=WORKSPACE-ID is required — listing is always scoped to one workspace. Add ?q= to search by title.

import requests

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

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/forms/?workspace=YOUR-WORKSPACE-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/forms/?workspace=YOUR-WORKSPACE-ID' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/forms/?workspace=YOUR-WORKSPACE-ID', {
    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.parse('https://hovercode.com/api/v2/forms/?workspace=YOUR-WORKSPACE-ID')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

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

response = http.request(request)
puts response.read_body
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

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

            URL url = new URL("https://hovercode.com/api/v2/forms/?workspace=YOUR-WORKSPACE-ID");
            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();
        }
    }
}

Response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "b1d3f5a7-0d77-4828-95e7-1c27c603c5c9",
      "title": "Newsletter signup",
      "status": "Published",
      "response_count": 128,
      "share_url": "https://hov.to/newsletter",
      "workspace": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "created": "2026-05-20T09:14:02.114820Z",
      "modified": "2026-06-01T12:00:00.000000Z"
    }
  ]
}

When you have no forms (or none match the filter), the response is {"message": "No results found", "results": []}.

GET List responses

https://hovercode.com/api/v2/forms/FORM-ID/responses/

A form's submissions, newest first. Each response includes its per-field answers.

import requests

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

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/forms/FORM-ID/responses/',
    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/forms/FORM-ID/responses/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/forms/FORM-ID/responses/', {
    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.parse('https://hovercode.com/api/v2/forms/FORM-ID/responses/')
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
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ListFormResponses {
    public static void main(String[] args) {
        try {
            String formId = "FORM-ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/forms/" + formId + "/responses/");
            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();
        }
    }
}

Response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "f0a1c2d3-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
      "submitted_at": "2026-06-02T18:30:00.000000Z",
      "is_complete": true,
      "completion_time_seconds": 42,
      "field_responses": [
        { "field_id": "a1b2c3d4-...", "field_label": "Name", "field_type": "text", "value": "Ada Lovelace" },
        { "field_id": "c3d4e5f6-...", "field_label": "Email", "field_type": "email", "value": "ada@example.com" }
      ]
    }
  ]
}

GS1 Digital Link

A GS1 Digital Link encodes one identifier (e.g. a GTIN) that resolves to a destination. The quickest way to make one is a single call to the QR create endpoint — pass an identifier and a destination and you get a working GS1 QR back.

The endpoints below are for advanced use: managing an identifier's multiple link types (product info, instructions, recalls…) over time, and reusing one identifier across several codes. You don't need them for a basic GS1 QR.

An identifier is unique per (domain, identifier, batch/serial) — the domain is set automatically and can't be changed (it's part of the scannable URL). Registering the same identifier again updates it rather than creating a duplicate.

POST Create a GS1 product

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

ParameterRequiredDescription
workspaceRequiredYour workspace ID.
identifier_valueRequiredThe identifier value, e.g. a GTIN. Validated (incl. GS1 check digit).
identifier_typeDefaults to "01"The GS1 Application Identifier. One of: 01 (GTIN), 00 (SSCC), 414 (GLN), 417 (Party GLN), 253 (GDTI), 255 (GCN), 401 (GINC), 402 (GSIN), 8003 (GRAI), 8004 (GIAI), 8006 (ITIP), 8013 (GMN), 8017 (GSRN – Provider), 8018 (GSRN – Recipient).
batch_lot, serial_numberOptionalQualifiers added to the Digital Link path.
expiry_dateOptionalYYMMDD format (e.g. 261231).
destination_urlOptionalSeeds a default product information page (gs1:pip) link.
import requests

data = {
    "workspace": "YOUR-WORKSPACE-ID",
    "identifier_type": "01",
    "identifier_value": "09506000134369",
    "destination_url": "https://example.com/product"
}

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

$data = array(
    "workspace" => "YOUR-WORKSPACE-ID",
    "identifier_type" => "01",
    "identifier_value" => "09506000134369",
    "destination_url" => "https://example.com/product"
);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/gs1/products/',
    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/gs1/products/' \
  -H 'Authorization: Token YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "workspace": "YOUR-WORKSPACE-ID",
    "identifier_type": "01",
    "identifier_value": "09506000134369",
    "destination_url": "https://example.com/product"
  }'
const axios = require('axios');

const data = {
    workspace: 'YOUR-WORKSPACE-ID',
    identifier_type: '01',
    identifier_value: '09506000134369',
    destination_url: 'https://example.com/product'
};

axios.post('https://hovercode.com/api/v2/gs1/products/', 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/gs1/products/')
data = {
  "workspace" => "YOUR-WORKSPACE-ID",
  "identifier_type" => "01",
  "identifier_value" => "09506000134369",
  "destination_url" => "https://example.com/product"
}

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("\"identifier_type\":\"01\",")
            .append("\"identifier_value\":\"09506000134369\",")
            .append("\"destination_url\":\"https://example.com/product\"")
            .append("}")
            .toString();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://hovercode.com/api/v2/gs1/products/"))
            .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();
        }
    }
}

Returns the product (201) with its Digital Link path and link types:

{
  "id": "7f1e2d3c-0d77-4828-95e7-1c27c603c5c9",
  "identifier_type": "01",
  "identifier_value": "09506000134369",
  "batch_lot": null,
  "serial_number": null,
  "expiry_date": null,
  "identifier_label": "GTIN",
  "digital_link_path": "/01/09506000134369",
  "link_types": [
    {
      "id": "a0b1c2d3-1111-2222-3333-444455556666",
      "link_type": "gs1:pip",
      "title": "Product information page",
      "destination_url": "https://example.com/product",
      "is_default": true,
      "language": null,
      "media_type": "text/html"
    }
  ],
  "created": "2026-06-01T15:02:26.343134Z"
}

GET List GS1 products

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

Your workspace's GS1 products (paginated), each with its nested link types.

import requests

response = requests.get(
    'https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/gs1/products/',
    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/gs1/products/',
    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/gs1/products/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/gs1/products/', {
    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.parse('https://hovercode.com/api/v2/workspace/YOUR-WORKSPACE-ID/gs1/products/')
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
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

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

            URL url = new URL("https://hovercode.com/api/v2/workspace/" + workspaceToken + "/gs1/products/");
            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();
        }
    }
}

GET PATCH DELETE A GS1 product

https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/

Retrieve, update the identifier/qualifiers, or delete (204) a product. The example below is a GET; PATCH and DELETE use the same URL.

import requests

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

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/gs1/products/PRODUCT-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/gs1/products/PRODUCT-ID/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/', {
    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.parse('https://hovercode.com/api/v2/gs1/products/PRODUCT-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
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class GetGS1Product {
    public static void main(String[] args) {
        try {
            String productId = "YOUR_PRODUCT_ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/gs1/products/" + productId + "/");
            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();
        }
    }
}

GET Get the linkset

https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/linkset/

An RFC 9264 linkset (application/linkset+json) — a preview of what the resolver serves. Optional ?domain= sets the anchor domain.

import requests

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

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/linkset/',
    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/gs1/products/PRODUCT-ID/linkset/' \
  -H 'Authorization: Token YOUR-TOKEN'
const axios = require('axios');

axios.get('https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/linkset/', {
    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.parse('https://hovercode.com/api/v2/gs1/products/PRODUCT-ID/linkset/')
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
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class GetGS1Linkset {
    public static void main(String[] args) {
        try {
            String productId = "YOUR_PRODUCT_ID";
            String token = "YOUR_TOKEN";

            URL url = new URL("https://hovercode.com/api/v2/gs1/products/" + productId + "/linkset/");
            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();
        }
    }
}

Response:

{
  "linkset": [
    {
      "anchor": "https://hov.to/01/09506000134369",
      "gs1:pip": [
        {"href": "https://example.com/product", "title": "Product information page", "type": "text/html"}
      ],
      "gs1:instructions": [
        {"href": "https://example.com/setup", "title": "Setup guide", "type": "text/html"}
      ]
    }
  ]
}

POST Create a GS1 QR code

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

Set qr_type to GS1 and pass a gs1_product object with an identifier_value (e.g. a GTIN) and a destination_url. This creates the GS1 product and the QR in one call. GS1 codes are always dynamic — the QR encodes the Digital Link URI, which the resolver redirects by link type.

Already created a product via the endpoints above? Pass its id as a string instead — "gs1_product": "PRODUCT-ID" — to reuse it.

import requests

data = {
    "workspace": "YOUR-WORKSPACE-ID",
    "qr_type": "GS1",
    "gs1_product": {
        "identifier_value": "09506000134369",
        "destination_url": "https://example.com/product"
    }
}

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_type" => "GS1",
    "gs1_product" => array(
        "identifier_value" => "09506000134369",
        "destination_url" => "https://example.com/product"
    )
);

$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_type": "GS1",
    "gs1_product": {
      "identifier_value": "09506000134369",
      "destination_url": "https://example.com/product"
    }
  }'
const axios = require('axios');

const data = {
    workspace: 'YOUR-WORKSPACE-ID',
    qr_type: 'GS1',
    gs1_product: {
        identifier_value: '09506000134369',
        destination_url: 'https://example.com/product'
    }
};

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_type" => "GS1",
  "gs1_product" => {
    "identifier_value" => "09506000134369",
    "destination_url" => "https://example.com/product"
  }
}

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_type\":\"GS1\",")
            .append("\"gs1_product\":{")
            .append("\"identifier_value\":\"09506000134369\",")
            .append("\"destination_url\":\"https://example.com/product\"")
            .append("}")
            .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();
        }
    }
}

The response includes a gs1_product summary and the Digital Link URI as qr_data.

Webhooks

Enable webhooks in your workspace API settings to receive a POST every time a dynamic QR code or short link is scanned. Available on the Business Plus plan.

Each request is application/json with an x-signature header. Verify it against your webhook secret, then return 200.


    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 payload is activity data:

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

This is a preview of the new docs. Send us feedback on the structure or anything missing.