diff --git a/experimental/uloapi/pom.xml b/experimental/uloapi/pom.xml
index f468e7e9acbdc4bb021121a4782641a3646545bd..dae17b2eb51192c5aaa54a271f81905f5f27d998 100644
--- a/experimental/uloapi/pom.xml
+++ b/experimental/uloapi/pom.xml
@@ -73,5 +73,10 @@
             <artifactId>gson</artifactId>
             <version>2.8.6</version>
         </dependency>
+        <dependency>
+            <groupId>com.ontotext.graphdb</groupId>
+            <artifactId>graphdb-free-runtime</artifactId>
+            <version>9.2.0</version>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/experimental/uloapi/src/main/java/info/mathhub/uloapi/config/Config.java b/experimental/uloapi/src/main/java/info/mathhub/uloapi/config/Config.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7bf0b4ae1007675f03f4604c87ddb47d45e59da
--- /dev/null
+++ b/experimental/uloapi/src/main/java/info/mathhub/uloapi/config/Config.java
@@ -0,0 +1,20 @@
+package info.mathhub.uloapi.config;
+
+import java.util.ResourceBundle;
+
+public class Config {
+    private Config() {};
+
+    public static String serverUrl() {
+        return Config.get("info.mathhub.uloapi.config.serverurl");
+    }
+
+    public static String repository() {
+        return Config.get("info.mathhub.uloapi.config.repository");
+    }
+
+    private static String get(String key) {
+        final ResourceBundle bundle = ResourceBundle.getBundle("uloapi");
+        return bundle.getString(key);
+    }
+}
diff --git a/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/GraphDB.java b/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/GraphDB.java
new file mode 100644
index 0000000000000000000000000000000000000000..0214febd481f0513363f3cd15ea6d2987967bde4
--- /dev/null
+++ b/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/GraphDB.java
@@ -0,0 +1,80 @@
+package info.mathhub.uloapi.query;
+
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryException;
+import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
+import org.eclipse.rdf4j.repository.manager.RepositoryManager;
+
+public class GraphDB {
+    private GraphDB() {};
+
+    static class OperationException extends RuntimeException {
+        OperationException(String message) {
+            super(message);
+        }
+
+        OperationException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * Functional interface that defines a function for running operations on
+     * a GraphDB database.
+     */
+    private interface Operation<T> {
+        /**
+         * Run some operation connected to a database.
+         *
+         * @param manager The manager that was used for initializing the database connections.
+         * @param repository The repository on which to operate.
+         * @param connection The connection to the repository on which to operate.
+         * @throws OperationException If the logic of the operation itself causes some kind of error.
+         * @throws RepositoryException If communication with the remote database fails.
+         */
+        T op(RepositoryManager manager, Repository repository, RepositoryConnection connection);
+    }
+
+    /**
+     * Run an operation on a remote repository.
+     *
+     * @param serverUrl The server of the remote GraphDB instance.
+     * @param repositoryName The repository on the remote instance on which to run the operation.
+     * @param op The operation to execute.
+     * @param <T> The return type of the operation
+     * @return The return value of the operation, i.e. of argument {@code op}.
+     * @throws OperationException If the logic of the operation itself causes some kind of error.
+     * @throws RepositoryException If communication with the remote database fails.
+     */
+    public static <T> T execute(String serverUrl, String repositoryName, Operation<T> op) {
+        RepositoryManager manager = null;
+        Repository repository = null;
+        RepositoryConnection connection = null;
+
+        try {
+            manager = new RemoteRepositoryManager(serverUrl);
+            manager.init();
+
+            if ((repository = manager.getRepository(repositoryName)) == null) {
+                throw new RepositoryException("no such repository");
+            }
+
+            connection = repository.getConnection();
+            return op.op(manager, repository, connection);
+        } finally {
+            if (connection != null) {
+                connection.close();
+            }
+
+            if (repository != null) {
+                repository.shutDown();
+            }
+
+            if (manager != null) {
+                manager.shutDown();
+            }
+        }
+    }
+}
+
diff --git a/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/ULO.java b/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/ULO.java
new file mode 100644
index 0000000000000000000000000000000000000000..75884ec67a4c2fde35507a0636bcd0f5d61c1e54
--- /dev/null
+++ b/experimental/uloapi/src/main/java/info/mathhub/uloapi/query/ULO.java
@@ -0,0 +1,39 @@
+package info.mathhub.uloapi.query;
+
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Namespace;
+import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.impl.SimpleNamespace;
+import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.model.vocabulary.FOAF;
+
+/**
+ * Constants for use with ULO, the Upper Library Ontology [1].
+ *
+ * [1] https://gl.mathhub.info/ulo/ulo/-/tree/master/
+ */
+public class ULO {
+    private ULO() {}
+
+    /**
+     * The ULO namespace: https://mathhub.info/ulo
+     */
+    public static final String NAMESPACE = "https://mathhub.info/ulo";
+
+    /**
+     * The recommended prefix for the ULO namespace: "ulo"
+     */
+    public static final String PREFIX = "ulo";
+
+    /**
+     * An immutable {@link Namespace} constant that represents the ULO namespace.
+     */
+    public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE);
+
+    public final static IRI INDUCTIVE_ON;
+
+    static {
+        final ValueFactory factory = SimpleValueFactory.getInstance();
+        INDUCTIVE_ON = factory.createIRI(ULO.NAMESPACE, "inductive-on");
+    }
+}
diff --git a/experimental/uloapi/src/main/resources/uloapi.properties b/experimental/uloapi/src/main/resources/uloapi.properties
new file mode 100644
index 0000000000000000000000000000000000000000..b67e220c850e1e1e15f99bec3a5f4f78697306ae
--- /dev/null
+++ b/experimental/uloapi/src/main/resources/uloapi.properties
@@ -0,0 +1,2 @@
+info.mathhub.uloapi.config.serverurl="http://graphdb:7200"
+info.mathhub.uloapi.config.repository="myulo"
\ No newline at end of file