Onion Search Engine Google | Darkweb search engine |Tor Website Search| onion website search | Onion Online

Darkurl API guidelines for market owners


Last updated: 10 Jan 2022

This document is designed to serve as a set of guidelines for darknet market owners / developers. The purpose of this guide is to create a better method of scraping such that Darkurl and smaller marketplaces can grow together.
As the title says, these are only guidelines. If you believe that something I am asking for is unreasonable, you are under no obligation to prepare code that will do what I ask, and I assure you that your market's listings will never be downgraded as a result of non-compliance with these guidelines.
All markets that follow these API guidelines are offered an incentive: if you meet all of the conditions outlined here, I will give all listings/vendors on your market a 15% match score boost, which should translate to more sales for your vendors and more commissions for you. If you cannot or will not meet all these guidelines, but you meet some of them, your boost will be less than 15%, but as I said before you will never be punished for running your marketplace the way you want to.

Once you have implemented these guidelines, please contact me via email at hackerdeveloper[at]protonmail.com and let me know any relevant information, so I can start implementing your API in my scrapers.

Request/response formatting

I would prefer a JSON-encoded response for all API requests. However, if you prefer to use another form of data encoding, I will work with you on your encoding method of choice.
If you decide to implement a captcha before API usage is allowed, this will be fine. Just include documentation on how to get the captcha image, and where to submit the solved captcha.

Recommended API URL tree

This is a recommendation on how to organize your API URLs. If this does not work for you, you can organize them however you would like. Just give us information about how you would prefer we access the API.

  • /api/search?page=page_number
  • *
  • /api/listing/identifier
  • *
  • /api/vendor/username
  • /api/vendor/username/feedback

Listing searches

I recommend the following format for /api/search. /api/search (no /page_number appended) should return an integer displaying how many pages of listings there are.
/api/search/page_number should return a JSON-encoded list of lists. Each list in the response content should include a minimum of the following information:

  • Some kind of identifier to be added to the end of /api/listing/ to get the full listing information
  • Listing title (string)
  • Vendor username (string)
  
http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search?page=2
        
{
  "current_page": 2,
  "data": [
    {
      "identifier": "11510570-29ba-11ec-af70-9b0652235f9a",
      "title": "SpotLoan Method Guide",
      "vendor": "Tempest1"
    },
    {
      "identifier": "1fa38760-4bba-11ec-a83c-a7475fe4d649",
      "title": "10 GR: OF PORSCHE BEST POLM BLONDE HASH IN THE DEEPWEB",
      "vendor": "deepmagic"
    }
  ],
  "first_page_url": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search?page=1",
  "from": 16,
  "last_page": 14,
  "last_page_url": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search?page=14",
  "next_page_url": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search?page=3",
  "path": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search",
  "per_page": 15,
  "prev_page_url": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/search?page=1",
  "to": 30,
  "total": 197
}
    

If you want, you can include the full listing content in the search response data, but either works.

Listings

Darkurl collects the following information about each listing:

  • Relative URL for listing (string)
  • Title (string)
  • Vendor username (string)
  • Description (string)
  • Relative URL(s) for image(s) (string)
  • Refund policy, if applicable (string)
  • Search tags, if applicable (string or JSON-encoded list)
  • Shipping options. Shipping options should be a JSON-encoded dictionary, where each item's key is a string indicating option text (e.g. "2 day USPS express") and each item's value is a float, indicating the price in the vendor's preferred local currency.
  • Product class - binary, 0 for physical products and 1 for digital products
  • Shipping origin (string)
  • Shipping destination(s) (string or JSON-encoded list)
  • Escrow type - integer, 0 for FE, 1 for normal escrow, 2 for multisig escrow
  • Local price in vendor's preferred currency (float)
  • Vendor's abbreviated preferred local currency (string, max 5 characters)
  • Whether this listing can be paid with BTC (boolean)
  • Whether this listing can be paid with LTC (boolean)
  • Whether this listing can be paid with BCH (boolean)
  • Whether this listing can be paid with XMR (boolean)
Example

Input
http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/listing/f82ba8c0-2068-11ec-9b80-014005219d49
Output
 
{
  "relative_URL": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/product/f82ba8c0-2068-11ec-9b80-014005219d49",
  "title": "NYS EWF BENEFITS 2021",
  "vendor_username ": "bluebat1",
  "description": "NEW YORK STATE EXCLUDED WORKERS FUND\r\nPAYING 30K USD",
  "relative_URL_image": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/storage/products/t5sBD7F1WOMWDcl0L55Z9JGv3O0Y6pQ2StwqOOEh.png",
  "shipping_options": "",
  "product_class": 1,
  "shipping_origin": "digital",
  "shipping_destination": "{\"AFG\":\"Afghanistan\",\"ALA\":\"\\u00c5land Islands\",\"ALB\":\"Albania\"}",
  "escrow_type ": "normal",
  "local_price ": 700,
  "local_currency": "$",
  "BTC": true,
  "LTC": false,
  "BCH": false,
  "XMR": false
} 

Vendors

Darkurl collects the following information about each vendor:

  • Username (string)
  • Relative URL for profile (string)
  • Relative URL for profile picture, if applicable (string)
  • "About" text (string)
  • Join date (integer) - number of seconds since Unix epoch
  • Number of sales (integer) - or number of feedback, if you are uncomfortable listing sales count
  • PGP key (string)


http://darkurlbr3nucbdlz5bfvpv4hvs2yj7fkanyv4yjqd.onion/api/vendor/mytrade15

{
  "username": "mytrade15",
  "relative_profile_URL": "http://darkurlbr3nucbdlz5bfvpv4hvs2yj7fkanyv4yjqd.onion/vendor/3931cb70-215d-11ec-b5e3-b74c6c11eaa0",
  "about": "",
  "join_date": 1632964443,
  "number_of_sales": 0,
  "PGP_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nxsBNBGC9J9UBCADfOlIwxJPY45UL\r\n=0GXu\r\n-----END PGP PUBLIC KEY BLOCK-----"
}
    

Feedback

Darkurl collects the following information about each piece of feedback:

  • Vendor username (string)
  • Relative URL of product being reviewed (string)
  • Timestamp (integer) - number of seconds since Unix epoch indicating when the review was left.
  • Score (integer) - -1 for a negative review, 0 for a neutral review, 1 for a positive review. If you operate on a 5-star system or some other type of ratings, just round the score in an appropriate way.
  • Value (float) - value of the purchase, preferably in BTC.
  • Review text (string)
  http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/api/vendor/spunky6boys/feedback
    


    
    
  {
    "vendor_username": "spunky6boys",
    "product_URL": "http://darkurldsmg2zs5k7vdo56efrgbgfeddfcifga7bgay3luetrhaiyd.onion/product/09bcc590-2f11-11ec-ba10-5d16391cc5db",
    "timestamp": 1635272979,
    "score": 1,
    "value": "110.00",
    "comment": "Wow this dude's amazing great comms crazy fast delivery and insane stealth and the gear holy shit. I mean it's better than most shit out there these days and market was having some issues so it delayed him a bit so he sent an extra g and his tar is crazy haven't seen shit that clean in a while all around good vendor and great product would highly recommend."
  }

    

    

 
    
    public function search() {
        $itemsPaginated = Product::with('user')->paginate(15);
        $itemsTransformed = $itemsPaginated
                        ->getCollection()
                        ->map(function ($item) {
                            return [
                        'identifier' => $item->id,
                        'listing_title' => $item->name,
                        'vendor_username ' => $item->user()->first()->username,
                            ];
                        })->toArray();

        $itemsTransformedAndPaginated = new \Illuminate\Pagination\LengthAwarePaginator(
                $itemsTransformed,
                $itemsPaginated->total(),
                $itemsPaginated->perPage(),
                $itemsPaginated->currentPage(), [
            'path' => \Request::url(),
            'query' => [
                'page' => $itemsPaginated->currentPage()
            ]
                ]
        );
        return Response::json($itemsTransformedAndPaginated, 200);
    }

    public function listing($identifier) {
        $listing = Product::with('user', 'physical')->where('id', $identifier)->first();
        $data = array(
            "listuning_URL" => URL::to('/') . '/product/' . $listing->id,
            "title" => $listing->name,
            "Vendor_username " => $listing->user()->first()->username,
            "Description" => $listing->description,
            "Relative URL image" => asset('storage/' . $listing->frontImage()->image),
            "Shipping_options" => "",
            "Product class" => $listing->isPhysical() ? 0 : 1,
            "Shipping origin" => $listing->isPhysical() ? $listing->specificProduct()->shipsFrom() : 'digital',
            "Shipping destination" => $listing->isPhysical() ? json_encode($listing->specificProduct()->countriesLong()) : json_encode(config('countries')),
            "Escrow type " => $listing->types,
            "Local price " => $listing->getLocalPriceFrom(),
            "local currency" => \App\Marketplace\Utility\CurrencyConverter::getLocalSymbol(),
            "BTC" => in_array('btc', $listing->getCoins()) ? true : false,
            "LTC" => in_array('ltc', $listing->getCoins()) ? true : false,
            "BCH" => in_array('bch', $listing->getCoins()) ? true : false,
            "XMR" => in_array('xmr', $listing->getCoins()) ? true : false,
        );
        return Response::json($data, 200);
    }

    public function vendor($user) {
        $user = User::query()->where('username', $user)->first();
        $data = array(
            "Username" => $user->username,
            "Relative URL for profile" => route('vendor.show', $user->id),
            "About" => $user->vendor->about,
            "Join date" => strtotime($user->vendor->created_at),
            "Number of sales" => $user->vendor->completedOrders(),
            "PGP key" => $user->hasPGP() ? $user->pgp_key : '',
        );

        return Response::json($data, 200);
    }

    public function vendorFeedback($user) {
        $user = User::query()->where('username', $user)->first();
        if ($user->vendor->hasFeedback()) {
            $countFeedback = $user->vendor->countFeedback();
            $feedbacks = array();
            foreach ($user->vendor->feedback()->get() as $feeback) {
                if ($feeback->type == 'positive') {
                    $Score = 1;
                } elseif ($feeback->type == 'neutral') {
                    $Score = 0;
                } else {
                    $Score = -1;
                }

                $data = array(
                    "Vendor username" => $user->username,
                    "Relative URL of product being reviewed" => route('product.show', $feeback->product_id),
                    "Timestamp" => strtotime($feeback->created_at),
                    "Score" => $Score,
                    "Value" => $feeback->product_value,
                    "Review text" => $feeback->comment,
                );
                $feedbacks[] = $data;
            }
        } else {
            $feedbacks = null;
        }
        return Response::json($feedbacks, 200);
    }