Get a free website with any plan

See how

Running AI-powered apps on ordinary hosting

The model runs on someone else's hardware. Your account makes an HTTPS call, holds a key and waits, which is why the interesting questions are about timeouts, not graphics cards.

Share

An AI feature looks exotic in a product meeting and ordinary in a server log. Someone types a question into a box on your site, your code adds an instruction and a key, posts a few kilobytes of JSON to api.openai.com or api.anthropic.com, and waits. Text comes back and you print it. That is the whole mechanic.

An app that calls an AI API does the model work on the provider's servers; the hosting account only needs to make HTTPS requests, store keys and serve the pages. Nothing on your plan multiplies a matrix. So the questions worth asking are not about hardware. They are about where the key lives, how long a request may take, and what happens when forty people click at once.

What an AI feature actually does on your server

Strip the branding off and the shape is familiar. Your code makes an outbound HTTPS request with a secret in the header, blocks while someone else does the expensive part, and renders the answer. Shared hosting has done exactly this for years against Stripe, Mailgun and every other API a small site touches.

Three consequences follow, and they are the entire engineering content of a wrapper app.

The work happens elsewhere, so your CPU is near idle during the call. A model generating 800 words of output costs the provider real GPU time and costs you a process sitting in wait. That is cheap in CPU and expensive in one specific resource: the slot that holds the request open.

The wait is long by web standards. A page assembles in 200 milliseconds; a model reply takes 2 to 20 seconds. Every timeout problem below comes from that single gap.

The key is the whole security story. No password, no login, no session: whoever holds the string can spend your money, which is why half of this guide is about file paths and panel screens rather than code.

A GPU server is needed only to run a model yourself, which is a different product from calling an API. Download Llama or Mistral weights and serve them and you are buying graphics cards, a real infrastructure project with its own cost curve. Renting a model through an API means someone else bought them. The two decisions get confused because both have "AI hosting" in the search box, and they have nothing in common.

Three runtimes on a shared plan: PHP, Node, Python

Pick the runtime your site already speaks. All three work.

PHP, the shortest path

If your site is WordPress or any PHP app, you already have everything. cURL is compiled in and the call is about ten lines.

<?php
require __DIR__ . '/../secrets/ai.php';   // defines AI_KEY, outside public_html

$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT        => 25,
  CURLOPT_HTTPHEADER     => [
    'Content-Type: application/json',
    'Authorization: Bearer ' . AI_KEY,
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'model'    => 'gpt-4o-mini',
    'messages' => [['role' => 'user', 'content' => $_POST['question']]],
  ]),
]);
$raw = curl_exec($ch);
$out = json_decode($raw, true);
echo htmlspecialchars($out['choices'][0]['message']['content'] ?? 'No answer.');

Two lines matter more than the rest. CURLOPT_TIMEOUT set below your PHP execution limit means cURL gives up first and you can print a readable message instead of a white screen. And the require at the top reaches up out of the web root, which is the whole key-storage strategy for PHP.

Node and Express, through cPanel

Node and Express servers run on Flashcloud shared hosting through cPanel's Setup Node.js App on Node 22 or 24, with NPM install and environment variables set from the panel. A minimal AI endpoint:

const express = require('express');
const app = express();
app.use(express.json());

app.post('/ask', async (req, res) => {
  const r = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-api-key': process.env.AI_KEY,          // from the cPanel panel
      'anthropic-version': '2023-06-01',
    },
    body: JSON.stringify({
      model: 'claude-sonnet-4-5',
      max_tokens: 500,
      messages: [{ role: 'user', content: req.body.question }],
    }),
  });
  const data = await r.json();
  res.json({ answer: data.content?.[0]?.text ?? '' });
});

app.listen(process.env.PORT);   // never a hard-coded 3000

We ran this shape on our own test account in October 2026, and the panel has three behaviours no tutorial mentions.

Setup Node.js App is not reachable from cPanel's Meridian theme, whose search returns nothing at all for "Node". Switch to the classic theme first, then search "Node" and the tool appears under Software. People conclude their plan lacks Node support when it is a theme away.

The LiteSpeed Node runner hands your app the full request path. With the Application URL set to example.com/api, a route you wrote as /menu answers nothing and the browser shows Cannot GET /api/menu. Mount the router on both paths, or give the app its own subdomain so the base path is /.

And stderr.log, in the application root, is where every startup crash lands. Before NPM Install had run, each request on our test wrote Error: Cannot find module 'express' into that file. Read it first. The full walkthrough is in running an Express or Node.js app with Setup Node.js App.

Python, for Flask, Django and FastAPI

Flask, Django and FastAPI apps run on Flashcloud shared hosting through cPanel's Setup Python App on Python 3.11, with pip install and environment variables set from the panel. Our October 2026 test went further than a panel screenshot: a Flask app on the test account answered a live request with {"framework":"flask","greeting":"Hello from the env var","ok":true,"python":"3.11.16"}, where greeting came from the panel's environment variables table and was read back through os.environ. That is the observed round trip, not a feature list.

import os, json, urllib.request
from flask import Flask, request, jsonify
app = Flask(__name__)

@app.post("/ask")
def ask():
    payload = json.dumps({
        "model": "gpt-4o-mini",
        "messages": [{"role": "user", "content": request.json["question"]}],
    }).encode()
    req = urllib.request.Request(
        "https://api.openai.com/v1/chat/completions", data=payload,
        headers={"Content-Type": "application/json",
                 "Authorization": "Bearer " + os.environ["AI_KEY"]})
    with urllib.request.urlopen(req, timeout=25) as r:
        data = json.load(r)
    return jsonify(answer=data["choices"][0]["message"]["content"])

Two panel details decide whether this runs. requirements.txt is not auto-detected: type the filename into the "Add another file and press enter" box in the Configuration files section, then Run Pip Install picks it up. And give a Python app its own subdomain. On our test, an app mounted at /flask under a domain running WordPress served its base URL fine, while /flask/menu returned WordPress's 404 page, because the parent folder's rewrite rules caught the path before the app saw it. Running a Flask or Django app with Setup Python App has the create form field by field.

Keys and secrets

This is the section that saves money. A key in the wrong place is a stranger's bill arriving on your card.

Node and Python: the panel. Both tools have an Environment variables table on the application screen. Add Variable, enter name and value, Done, Save, then Restart. The restart is not optional, and it is the most common support question on this path: Node reads process.env once at process start, so an edited variable is invisible to a process already running. Our test confirmed it end to end, a variable unreadable until the app restarted and then present on the next request. Node.js environment variables covers the symptoms, including the quiet failure where the app starts fine and falls back to a default.

PHP: a file above the web root. PHP has no equivalent panel table, which is why most shared-hosting tutorials are wrong the same way: they tell you to drop a .env next to index.php. That file is inside the document root, and one misconfiguration turns it into a URL. Put the secret one directory up instead, where no request can reach it:

/home/user/secrets/ai.php     <- define('AI_KEY', 'sk-...');
/home/user/public_html/       <- everything the web can see

Then require __DIR__ . '/../secrets/ai.php';. If you must keep a .env for framework reasons, keep it outside public_html too and read it with getenv().

Never in the frontend. Any variable named VITE_ or NEXT_PUBLIC_ is compiled into the JavaScript bundle the browser downloads. It is published, not configured. The same goes for a key pasted into a React component, an inline <script> tag or a chatbot widget's config object. Keys that leak this way turn up in automated scans within hours, and checking AI-written code for exposed secrets before it reaches a server is covered in AI-era website security for small sites.

The practical rule: the browser calls your server, your server calls the model. One extra hop, and the key never leaves the machine you control. A per-runtime walkthrough of exactly where each file goes is in progress.

Timeouts, streaming and background jobs

Here is the one genuinely awkward thing about AI on shared hosting, and it has three solutions depending on how long the work takes.

PHP on a Flashcloud shared plan can call AI APIs with cURL or an SDK; the limits that matter are max_execution_time and memory_limit, published in the knowledgebase. max_execution_time defaults to 30 seconds on most PHP installs and gates time rather than size, so a script that posts 200 bytes and waits for a 2,000-word answer can die while the transfer itself was trivial. PHP limits explained covers raising it to 120 to 300 seconds from the PHP options page, with the symptom pattern: request completes, page hangs, timeout or 500.

Raising the limit is the blunt fix. It works for a short answer and fails for a long one, because a visitor staring at a blank page for 40 seconds has already left.

Stream instead. Every major provider supports server-sent events, and streaming changes the user's experience far more than it changes your code. Set stream: true in the request, then forward each chunk as it arrives:

header('Content-Type: text/event-stream');
header('X-Accel-Buffering: no');          // stop the proxy holding chunks
while (ob_get_level()) ob_end_flush();

curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) {
    echo $chunk;
    flush();
    return strlen($chunk);
});
curl_exec($ch);

First tokens appear in under a second, and the execution limit stops being the binding constraint because the connection is producing output the whole time. Streaming on PHP has sharp edges around output buffering and LiteSpeed, and a dedicated guide is planned.

Or do not make the visitor wait at all. Some jobs have no business inside a page load: summarising 400 product descriptions, generating alt text for a media library, drafting a weekly digest. Write the request to a database table, return immediately, and let cron do the work. Most shared plans cap cron at a five-minute minimum, plenty for queue-shaped work, and setting up cron jobs has the syntax plus the two details that trip everyone: absolute paths to /usr/local/bin/php, and > /dev/null 2>&1 so a five-minute job does not mail you 288 times a day. Background AI tasks get their own article later in this series.

The decision in one line: under 10 seconds, raise the limit; user-facing and longer, stream; not user-facing at all, cron it.

Sizing: what a wrapper app needs

The question people ask is how much RAM. The question that matters is how many requests can be open at the same time.

A model call holds a process for several seconds while using almost no CPU. On a shared plan the ceiling you meet is entry processes, the number of simultaneous requests your account may have in flight, not memory and not cores. Ten visitors clicking a chat button at once is ten slots held for five seconds each, a different load profile from ten visitors loading a cached WordPress page where each slot is freed in a fifth of a second.

Which gives three sizing rules.

Count concurrency, not traffic. A feature used 500 times a day with no overlap is invisible. The same 500 calls arriving in a ten-minute burst after an email campaign is a queue. Cron moves that burst off the request path entirely.

Watch the right graph. Checking your hosting resource usage shows entry processes and CPU over time, and resource limits explained explains what each ceiling gates. A plan change based on that graph is a decision; one based on the word "AI" is a purchase.

Memory is rarely the problem. The payload is text, and a 4,000-token exchange is a few tens of kilobytes. You hit memory_limit by decoding something enormous, not by talking to a model.

Flashcloud shared hosting starts at $2.79 a month on an annual term and every plan includes a free domain for life, free SSL, mailboxes, daily backups with 30-day retention and human support. Start, at 2 CPU cores and 2 GB RAM, runs a site with an AI feature on it. Grow, at 4 cores and 4 GB, is the honest pick when the AI feature is the product rather than a convenience, because concurrency headroom is what you are buying. The Node.js hosting page lists what the runtime gets, and Python plans carry the same Setup Python App tool.

Costs: the API bill against the hosting bill

Two invoices, and they behave in opposite ways. Hosting is fixed and small, and you know it a year ahead.

The API bill is usage-based and unbounded by default. It scales with tokens, so it scales with how many people use the feature and how verbose you let the model be. A chat endpoint with no cap on a public page is an open tap, and the failure mode is not a slow site, it is a four-figure invoice from a provider with no dispute process.

So the hosting decision and the AI decision are separate, and spending more on hosting does not make the API cheaper. What does: a spend limit in the provider's dashboard on day one, a smaller model for routine work, a short max_tokens, rate limiting per visitor, and caching repeated questions, because a FAQ bot answers the same forty things forever and the second answer can come from your own database for nothing.

Sizing an app by what it actually does, including the cost side, gets its own article in this series.

When shared is not enough

Shared hosting runs an AI feature. It does not run everything, and the thresholds are specific. Move to a VPS when one of these is true:

  • A process has to stay running. A queue worker that never exits, a websocket layer holding thousands of connections, a scheduler finer than cron allows. Shared hosting starts a process per request and shared cron does not go below five minutes, so a persistent worker has nowhere to live.
  • You want the database engine itself. Your own Postgres with pgvector for embeddings, or Redis for a response cache. MySQL on a shared plan is managed for you, which is the point of it and also the limit of it.
  • The build has to run on the server. Shared plans have no shell for npm run build, so the output has to be committed or uploaded. If your team cannot work that way, you need a shell.
  • You are running the model. Serving weights yourself is the one case that genuinely needs GPUs, a different product at a different price.

A chatbot, a content helper, a classifier, a summariser, an AI search box: none are on that list. They are HTTPS calls, and the account is already good at HTTPS calls. When a graph says otherwise, VPS hosting is the step up, and the threshold case gets its own article.

Start here: the smallest working example

Do not build the product first. Build the proof, in twenty minutes.

  1. Get a key from the provider and set a spend limit on the account before you write a line of code.
  2. Put the key where it belongs: the environment variables table in Setup Node.js App or Setup Python App, or a PHP file above public_html.
  3. Write one endpoint that takes one question and returns one answer. No streaming, no history, no UI.
  4. Call it from a form. Confirm the answer appears and that the key is nowhere in view source.
  5. Read stderr.log if anything breaks. On our own tests it named the problem every time.
  6. Then add streaming, a cron queue or a cache, in whatever order your app needs.

If the app came out of an AI tool rather than your own editor, the frontend and deploy path are covered in hosting AI-built apps from Lovable, Bolt, v0 and Cursor, and the same key rules apply to whatever the tool wrote. If the feature goes into WordPress as a plugin, AI and WordPress covers what an AI plugin costs the server. Whether you want an assistant with access to the hosting account while you work is a separate question, answered in the AI agents and your hosting account guide.

The short version: the model is not your problem. The key, the clock and the concurrency are. Build the endpoint tonight; Node.js hosting on a shared plan is where the smallest version above runs, and the sizing table tells you when it stops being enough.

Nickola Naous
About the author
Nickola NaousCo-Founder

Nickola Naous is the co-founder of Flashcloud and the technical half of the team. He's spent nearly two decades building and running web hosting infrastructure, and co-founded and sold a hosting company before starting Flashcloud. He writes about performance, servers, security, and how AI is reshaping the technology behind a fast, reliable website.

Connect on LinkedIn

Better web hosting starts here.

Free domain for life, free starter website, free migration, daily backups and 24/7 human support, with an anytime money-back guarantee.

Frequently asked questions