diff --git a/api-client/.gitignore b/api-client/.gitignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/api-client/.gitignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/api-client/index.cjs b/api-client/index.cjs
new file mode 100644
index 0000000..14723dd
--- /dev/null
+++ b/api-client/index.cjs
@@ -0,0 +1,70 @@
+const fetch = require('node-fetch')
+
+function url_join(base, endpoint){
+    return base + endpoint
+    //return new URL(endpoint, base).href;
+}
+
+class APIWrapper {
+    #base;
+    constructor(base_url){
+        this.#base = base_url.toString()
+    }
+
+    async query(endpoint, options={}){
+        const url = url_join(this.#base, endpoint);
+        return await fetch(url, options)
+            .then(res=>res.json())
+            .then(res=>{if(res.data)return res.data; throw new Error(res.message)});
+    }
+
+    get(endpoint){
+        return this.query(endpoint);
+    }
+
+    post(endpoint, body={}){
+        return this.query(endpoint, {
+            method: 'post',
+            headers: {'Content-Type': 'application/json'},
+            body: JSON.stringify(body)
+        })
+    }
+
+    delete(endpoint, body={}){
+        return this.query(endpoint, {
+            method: 'delete',
+            headers: {'Content-Type': 'application/json'},
+            body: JSON.stringify(body)
+        })
+    }
+
+    get url_base(){
+        return this.#base
+    }
+}
+
+
+class PistonEngine extends APIWrapper {
+    constructor(base_url = 'http://127.0.0.1:6969'){
+        super(base_url);
+    }
+
+    run_job({language, version, files, main, args, stdin, compile_timeout, run_timeout}){
+        return this.post(`/jobs`, {language, version, files, main, args, stdin, compile_timeout, run_timeout})
+    }
+
+    list_packages(){
+        return this.get('/packages').then(x=>x.packages)
+    }
+
+    install_package({language, version}){
+        return this.post(`/packages/${language}/${version}`);
+    }
+
+    uninstall_package({language, version}){
+        return this.post(`/packages/${language}/${version}`);
+    }
+}
+
+
+module.exports = {PistonEngine}
\ No newline at end of file
diff --git a/api-client/package.json b/api-client/package.json
new file mode 100644
index 0000000..a0f144d
--- /dev/null
+++ b/api-client/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "piston-api-client",
+  "version": "1.0.0",
+  "description": "Wraps API of Piston Engine API",
+  "main": "index.cjs",
+  "author": "Thomas Hobson <thomas@hexf.me>",
+  "license": "MIT",
+  "dependencies": {
+    "node-fetch": "^2.6.1"
+  }
+}
diff --git a/cli/commands/execute.js b/cli/commands/execute.js
index 3d04ec6..8d5ed69 100644
--- a/cli/commands/execute.js
+++ b/cli/commands/execute.js
@@ -1,4 +1,4 @@
-//const fetch = require('node-fetch');
+const {PistonEngine} = require('piston-api-client');
 const fs = require('fs');
 const path = require('path');
 const chalk = require('chalk');
@@ -34,6 +34,8 @@ exports.builder = {
 
 exports.handler = async function(argv){
     
+    const api = new PistonEngine(argv['piston-url']);
+
     const files = [...(argv.files || []),argv.file]
         .map(file_path => ({
             name: path.basename(file_path),
@@ -48,7 +50,7 @@ exports.handler = async function(argv){
     })) || "";
 
 
-    const request = {
+    const response = await api.run_job({
         language: argv.language,
         version: argv['language-version'],
         files: files,
@@ -57,10 +59,7 @@ exports.handler = async function(argv){
         stdin,
         compile_timeout: argv.ct,
         run_timeout: argv.rt
-    };
-
-    let response = await argv.axios.post('/jobs', request);
-    response = response.data
+    })
 
     function step(name, ctx){
         console.log(chalk.bold(`== ${name} ==`))
diff --git a/cli/commands/ppman_commands/install.js b/cli/commands/ppman_commands/install.js
index 151ab1d..39d0a28 100644
--- a/cli/commands/ppman_commands/install.js
+++ b/cli/commands/ppman_commands/install.js
@@ -1,3 +1,4 @@
+const {PistonEngine} = require('piston-api-client');
 const chalk = require('chalk');
 
 exports.command = ['install <language> <language-version>']
@@ -12,12 +13,15 @@ const msg_format = {
 
 }
 
-exports.handler = async function({axios, language, languageVersion}){
-    try{
-        const install = await axios.post(`/packages/${language}/${languageVersion}`)
-        
-        console.log(msg_format.color(install.data));
-    }catch({response}){
-        console.error(response.data.message)
-    }
+exports.handler = async function(argv){
+    const api = new PistonEngine(argv['piston-url']);
+
+    const opts = {
+        language: argv['language'],
+        version: argv['language-version']
+    };
+
+    const install = await api.install_package(opts).catch(x=>x);
+    
+    console.log(msg_format.color(install));
 }
\ No newline at end of file
diff --git a/cli/commands/ppman_commands/list.js b/cli/commands/ppman_commands/list.js
index 5d6584e..4a44dcf 100644
--- a/cli/commands/ppman_commands/list.js
+++ b/cli/commands/ppman_commands/list.js
@@ -1,4 +1,4 @@
-//const fetch = require('node-fetch');
+const {PistonEngine} = require('piston-api-client');
 const chalk = require('chalk');
 
 exports.command = ['list']
@@ -13,12 +13,13 @@ const msg_format = {
 
 }
 
-exports.handler = async function({axios}){
+exports.handler = async function(argv){
+    const api = new PistonEngine(argv['piston-url']);
 
-    const packages = await axios.get('/packages');
+    const packages = await api.list_packages();
 
-    
-    const pkg_msg = packages.data.data.packages
+   
+    const pkg_msg = packages
         .map(msg_format.color)
         .join('\n');
 
diff --git a/cli/index.js b/cli/index.js
index 8bb17e0..ff29736 100755
--- a/cli/index.js
+++ b/cli/index.js
@@ -1,15 +1,4 @@
-#!/usr/bin/env node
-
-const axios = require('axios').default;
-
-const axios_instance = function(argv){
-    argv.axios = axios.create({
-        baseURL: argv['piston-url']
-    });
-
-    return argv;
-};
-
+#!/usr/bin/node
 require('yargs')(process.argv.slice(2))
     .option('piston-url', {
         alias: ['u'],
@@ -17,10 +6,9 @@ require('yargs')(process.argv.slice(2))
         desc: 'Piston API URL',
         string: true
     })
-    .middleware(axios_instance)
     .scriptName("piston")
     .commandDir('commands')
     .demandCommand()
     .help()
     .wrap(72)
-    .argv;
+    .argv
diff --git a/cli/package.json b/cli/package.json
index 5d89dfd..ca8f8f5 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -6,8 +6,8 @@
   "author": "Thomas Hobson <thomas@hexf.me>",
   "license": "MIT",
   "dependencies": {
-    "axios": "^0.21.1",
     "chalk": "^4.1.0",
+    "piston-api-client": "file:../api-client",
     "yargs": "^16.2.0"
   }
 }
diff --git a/cli/yarn.lock b/cli/yarn.lock
index 2d24666..d6958b1 100644
--- a/cli/yarn.lock
+++ b/cli/yarn.lock
@@ -14,13 +14,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
-axios@^0.21.1:
-  version "0.21.1"
-  resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
-  integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
-  dependencies:
-    follow-redirects "^1.10.0"
-
 chalk@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
@@ -60,11 +53,6 @@ escalade@^3.1.1:
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
   integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
-follow-redirects@^1.10.0:
-  version "1.13.3"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
-  integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
-
 get-caller-file@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@@ -80,15 +68,25 @@ is-fullwidth-code-point@^3.0.0:
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
 
+node-fetch@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
+  integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
+
+"piston-api-client@file:../api-client":
+  version "1.0.0"
+  dependencies:
+    node-fetch "^2.6.1"
+
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
   integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
 
 string-width@^4.1.0, string-width@^4.2.0:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
-  integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
+  integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
   dependencies:
     emoji-regex "^8.0.0"
     is-fullwidth-code-point "^3.0.0"
@@ -123,9 +121,9 @@ y18n@^5.0.5:
   integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
 
 yargs-parser@^20.2.2:
-  version "20.2.7"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
-  integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
+  version "20.2.6"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
+  integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
 
 yargs@^16.2.0:
   version "16.2.0"
diff --git a/shell.nix b/shell.nix
deleted file mode 100644
index f9063bc..0000000
--- a/shell.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-{ pkgs ? import <nixpkgs> {} }:
-  pkgs.mkShell {
-    # nativeBuildInputs is usually what you want -- tools you need to run
-    nativeBuildInputs = [ pkgs.nodejs-15_x pkgs.yarn ];
-}
\ No newline at end of file